У меня есть _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
Я хотел бы понять, что может быть причиной этих проблем, потому что я понятия не имею. Возможно, я делаю что-то неправильно.
Я попытался попросить чатгпт о решении. Время потрачено впустую.
Также старайтесь избегать создания/удаления, используйте std::make_unique. И const char* image_path;
бесполезно std::string
от этого. И действительно, прекратите использовать GOTO. Тот, кто учил вас C++, не очень хорошо справился со своей задачей. Поэтому, прежде чем продолжить (создавать игру), сделайте шаг назад и изучите еще немного основ C++ (например, Learncpp.com). А затем узнайте об абстрактных базовых классах, внедрении зависимостей и возможности модульного тестирования вашего кода (игры имеют тенденцию становиться большими и поэтому действительно нуждаются в предварительном проектировании).
Обязательное предупреждение. Имена, начинающиеся с подчеркивания, за которым следует заглавная буква, зарезервированы для компилятора и не должны использоваться в пользовательском коде.
Я не вижу причин для сбоя, но код ужасающий. Постарайтесь не полагаться на такое количество указателей.
Вы можете сделать одну простую вещь: поскольку вызов конструктора копирования или оператора присваивания для вашего объекта не является хорошей идеей, вам следует удалить эти методы _Object(const _Object&) = delete;
и _Object& operator=(const _Object&) = delete;
. Если это вызывает какие-либо ошибки компилятора, значит, вы нашли ошибку.
Дюп: Порядок наследования и инициализации C++
Речь идет о порядке инициализации. Базовый класс инициализируется первым, а переменная пути производного класса — только позже. Это дает базовому классу неинициализированный указатель.
В конструкторе без делегирования инициализация выполняется в следующий порядок:
— Во-первых, и только для конструктора наиболее производный класс (6.7.2), виртуальные базовые классы инициализируются в порядке, в котором они появляются при обходе слева направо в глубину. направленный ациклический граф базовых классов, где «слева направо» — порядок появления базовых классов в производном классе список-базовых спецификаторов.
— Тогда прямые базовые классы инициализируются в порядке объявления, как они появляются в список базовых спецификаторов (независимо от порядка инициализаторов памяти).
— Затем нестатические элементы данных инициализируются в порядке они были объявлены в определении класса (опять же независимо от порядок мем-инициализаторов).
— Наконец, выполняется составной оператор тела конструктора.
[Примечание 6: порядок декларации призван гарантировать, что база и член подобъекты уничтожаются в порядке, обратном инициализации. -конец примечание]
Предположительно, создание статического Button::image
решило бы эту проблему.
Я передал path::img::menu_button
непосредственно конструктору, и это сработало.
class _Button : public _Object {
public:
_Button(SDL_Renderer* rend, int_fast64_t z_order, int x, int y)
: _Object(rend, z_order, path::img::menu_button, x, y) {}
/* it works! */
Ваш отладчик точно скажет вам, что вызывает сбой. Кроме того: идентификаторы, начинающиеся с подчеркивания и заглавной буквы, зарезервированы, их использование является неопределенным поведением. Еще одно замечание:
new
и необработанные указатели вышли из моды уже добрые десять лет.