我有
_Object
这是一个父类,我有 _Button
这是一个子类。这是我可怕的代码:
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);
}
};
问题是,如果我尝试创建
_Object
的实例,它工作得很好:
_Object* my_object = new _Object(rend, 69, "whatever.png", 0, 0); //fine
但是当我尝试创建
_Button
的实例时,程序崩溃了:
_Button* my_button = new _Button(rend, 420, 0, 0); //crash
我还注意到复制构造函数也不起作用:
_Object* my_object = new _Object(rend, 69, "whatever.png", 0, 0); //fine
_Object* my_object_2 = new _Object(rend, my_object); //crash
我想了解可能导致这些问题的原因,因为我不知道。也许我做错了什么。
我尝试向chatgpt 寻求解决方案。浪费了时间。
这是关于初始化的顺序。首先初始化基类,然后才初始化派生类的路径变量。这给了基类一个未初始化的指针。
在非委托构造函数中,初始化在 以下顺序:
— 首先,且仅适用于 大多数派生类(6.7.2),虚拟基类在中初始化 它们在深度优先从左到右遍历时出现的顺序 基类的有向无环图,其中“从左到右”是 基类在派生类中的出现顺序 基本说明符列表。
— 那么,直接基类是 按照声明顺序初始化,因为它们出现在 基本说明符列表(无论内存初始化器的顺序如何)。
— 然后,非静态数据成员按顺序初始化 它们是在类定义中声明的(同样,无论 内存初始化器的顺序)。
—最后, 执行构造函数主体的复合语句。
[注6: 声明令的目的是确保基地和成员 子对象以与初始化相反的顺序销毁。 -结尾 注]