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.
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.
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]