c++oopinheritancecrash

Crashes while trying to create an instance of a child class


I have _Object which is a parent class and i have _Button which is a child class. Here's my terrifying code:

class _Object {
protected:
    SDL_Texture* texture;
    const char* image_path;
    static uint64_t count; /* 18,446,744,073,709,551,615 objects ought to be enough for anybody */
    uint_least32_t group;
    static std::set<int_fast64_t> z_order_set;
    int_fast64_t z_order;
    int64_t x;
    int64_t y;
    uint8_t alpha;
    uint16_t rot;
    float scale;
    static int_fast64_t uniquify_z_order(int_fast64_t& z_order) {
        if (z_order >= 0) {
            while (z_order_set.find(z_order) != z_order_set.end()) {
                z_order++;
                if (z_order == INT_FAST64_MAX) {
                    goto EXCEPTION;
                }
            }
        } else {
            while (z_order_set.find(z_order) != z_order_set.end()) {
                z_order--;
                if (z_order == INT_FAST64_MIN) {
                    goto EXCEPTION;
                }
            }
        }
        return z_order;
EXCEPTION:
        z_order = 0;
        return 0;
    }
public:
    _Object() = delete;
    _Object(SDL_Renderer* rend, int_fast64_t z_order, const char* image, int x, int y) {
        _constructor(rend, z_order, image, x, y, ((uint8_t)255), ((uint16_t)0), 100.0f);
    }
    _Object(SDL_Renderer* rend, int_fast64_t z_order, const char* image, int x, int y, uint8_t alpha, uint16_t rot, float scale) {
        _constructor(rend, z_order, image, x, y, alpha, rot, scale);
    }
    _Object(SDL_Renderer* rend, _Object* obj) {
        _constructor(rend, obj->z_order, obj->image_path, obj->x, obj->y, obj->alpha, obj->rot, obj->scale);
    }
    ~_Object() {
        SDL_DestroyTexture(this->texture);
    }
    /* Constructor */
    void _constructor(SDL_Renderer* rend, int_fast64_t z_order, const char* image_path, int x, int y, uint8_t alpha, uint16_t rot, float scale) {
        if (z_order != 0) {
            if (z_order_set.find(z_order) != z_order_set.end()) {
                stacktrace(module::warning, "z_order (%lld) is already in use. New unique z_order: %lld", z_order, uniquify_z_order(z_order));
                if (z_order == 0) {
                    stacktrace(module::error, "No free z_order left. Object discarded.");
                    this->z_order_set.erase(z_order);
                    throw OUT_OF_Z_ORDER;
                }
            }
            this->z_order_set.insert(z_order);
            this->z_order = z_order;
        } else {
            stacktrace(module::error, "Attempted to use zero as a z_order (reserved for player). Object discarded.");
            throw OUT_OF_Z_ORDER;
        }
        this->texture = IMG_LoadTexture(rend, image_path);
        if (!this->texture) {
            stacktrace(module::error, "Couldn't load \"%s\". Object discarded.", image_path);
            this->z_order_set.erase(z_order);
            throw;
        }
        this->x = x;
        this->y = y;
        this->alpha = alpha;
        this->rot = rot;
        this->scale = scale;
    }
    /* Methods */
    SDL_Texture* get_texture(void) const {
        return this->texture;
    }
    std::pair<int64_t, int64_t> get_position(void) const {
        return {this->x, this->y};
    }
    void move_to(int64_t x, int64_t y) {
        this->x = x;
        this->y = y;
    }
    void move_from(int x, int y) {
        this->x += x;
        this->y += y;
    }
};

uint64_t _Object::count = 0;
std::set<int_fast64_t> _Object::z_order_set;

class _Button : public _Object {
    const char* image = path::img::menu_button; //the path is valid, i double-checked
public:
    _Button() = delete;
    _Button(SDL_Renderer* rend, int_fast64_t z_order, int x, int y)
        : _Object(rend, z_order, image, x, y) {}
    _Button(SDL_Renderer* rend, int_fast64_t z_order, int x, int y, uint8_t alpha, uint16_t rot, float scale)
        : _Object(rend, z_order, image, x, y, alpha, rot, scale) {}
    ~_Button() = default;
    bool was_clicked(int mouse_x, int mouse_y) {
        int txtrw, txtrh;
        SDL_QueryTexture(this->get_texture(), NULL, NULL, &txtrw, &txtrh);
        auto [xpos, ypos] = this->get_position();
        return (mouse_x >= xpos && mouse_x <= xpos + txtrw && mouse_y >= ypos && mouse_y <= ypos + txtrh);
    }
};

The problem is that i if i try to create an instance of _Object it works perfectly fine:

_Object* my_object = new _Object(rend, 69, "whatever.png", 0, 0); //fine

But when i'm trying to create an instance of _Button the program crashes:

_Button* my_button = new _Button(rend, 420, 0, 0); //crash

I also noticed that copy construtor doesn't work, too:

_Object* my_object = new _Object(rend, 69, "whatever.png", 0, 0); //fine
_Object* my_object_2 = new _Object(rend, my_object); //crash

I would like to understand what could be causing these issues because i have no idea. Perhaps i'm doing something wrong.

I tried asking chatgpt for a solution. Time wasted.


Solution

  • It is about order of initialization. The base class is initialized first, and the derived class' path variable only later. That gives the base class an uninitialized pointer.

    The standard says:

    In a non-delegating constructor, initialization proceeds in the following order:

    — First, and only for the constructor of the most derived class (6.7.2), virtual base classes are initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes, where “left-to-right” is the order of appearance of the base classes in the derived class base-specifier-list.

    — Then, direct base classes are initialized in declaration order as they appear in the base-specifier-list (regardless of the order of the mem-initializers).

    — Then, non-static data members are initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).

    — Finally, the compound-statement of the constructor body is executed.

    [Note 6 : The declaration order is mandated to ensure that base and member subobjects are destroyed in the reverse order of initialization. —end note]