Почему мой указатель "this" внутри функции-члена имеет значение null? - PullRequest
0 голосов
/ 05 октября 2018

Я не хочу показывать вам книгу, поэтому собираюсь упростить.У меня есть класс под названием «Плитка», который выглядит примерно так:

class Tile{
public:
   struct Type{ unsigned int uvIndex; ... };
   Tile(Tile::Type* tt){ tt_ = tt; ... }
   Type tt_ = nullptr;
   Tile* getNorthEast(){
       printf("this %p\n", this); //for debugging purposes
       /* calculation; "this" pointer is need for that  */
       return northEastTilePtr;
   }
};

Я хочу разместить многие из них в одном большом массиве, который никогда не будет перемещаться снова (не нужно a), поэтому я делаю это распределениевручную

//Manual allocation to prevent constructor from being called
Tile* tiles = (Tile*)malloc(numTiles*sizeof(Tile));
if(tiles == nullptr) throw std::bad_alloc();

Так как тип Tile сначала не известен, я не могу вызвать new, чтобы сделать это.Теперь у меня есть выделенная память, но конструкторы не называются.Генератор мира запускается и вызывает для каждой плитки

tiles[tileIndex] = Tile(tileTypePtr);

Теперь он должен был создать все объекты всех типов.Если я отрисовываю сцену и без вызова getNorthEast();, я вижу, что типы были установлены правильно из-за uvIndex (это просто указывает, какой участок текстуры визуализировать).Таким образом, tt_ установлен правильно, и конструктор должен работать правильно.Тем не мение!Если я сейчас вызываю getNorthEast();, оператор printf говорит мне, что указатель this равен 00000000 .Это портит расчет и приводит к сбою.

Возможно, это связано с неопределенным поведением, но я не вижу, что я делаю неправильно ... Мне нужна ваша помощь для этого.

РЕДАКТИРОВАТЬ 1: Итак, я посмотрелпри размещении нового.Я изменил распределение и назначение на:

//allocation 
tiles = (Tile*)new char[numTiles*sizeof(Tile)];
//assignment
new (&tiles[tileIndex]) Tile(tileTypePtr);

Однако this все еще nullptr.На этот раз назначение должно быть полностью перезаписано без неопределенного поведения.Распределение по сути делает то же самое, что и std::vector.reserve();

Дополнительная информация: getNorthEast () вызывается намного позже в основном цикле.Прямо сейчас это просто инициализация.Поэтому он не должен влиять на то, построен объект или нет.

Правка 2: Я только что повторил попытку, используя вектор.тот же результат.

std::vector<Tile> tiles;
//allocation
tiles_.reserve(numTiles);
//construction for each index
tiles.emplace_back(Tile(tileTypePtr)); 

Поскольку ни вектор, ни ручная работа не работали даже с вашим вводом, я начинаю подозревать, что причиной является то, чего я не упомянул.Я буду продолжать искать, пока не найду что-то еще, что может вызвать проблемы.

1 Ответ

0 голосов
/ 05 октября 2018
tiles[tileIndex] = Tile(tileTypePtr);

Для этого создается временный экземпляр Tile, а затем (move-) назначает этот временный экземпляр в экземпляр Tile, который (предполагается) существует в массиве.,Но этот экземпляр Tile не существует, поэтому поведение программы не определено.

Вот правильный способ создания объекта в существующий буфер памяти:

 // you can use malloc, but I see no reason to need it
 unsigned char *buffer = new unsigned char[numTiles*sizeof(Tile)];
 auto offset = sizeof(Tile) * tileIndex;
 Tile* tptr = new(buffer + offset) Tile(tileTypePtr);

Этот синтаксис, который повторно использует память для нового объекта, называется "размещение нового".Вы должны включить заголовок <new>, чтобы использовать его.

Однако нет необходимости повторно реализовывать этот буфер для повторного использования самостоятельно.std::vector<Tile> делает это для вас, помимо заботы об управлении опасной памятью, а также тонкостях правил безопасности исключений и наложения указателей.Вы уже споткнулись на одну оговорку.Нет необходимости прыгать на следующий.std::vector обычно является идеальным решением для динамических массивов типов, которые не могут быть построены по умолчанию.


Редактировать: приведенный ниже ответ основан на моей первоначальной интерпретации того, что вы собираетесь иметь объекты разных типов в вашеммассив.Теперь я замечаю, что у вас есть Type указатель в вашем объекте, и вы полагаете, что на самом деле вы можете хранить только Tile экземпляры с другим внутренним Type указателем (я предполагаю, что tt_ должен быть указателем).

Однако ваша структура кажется немного шаткой.Рассматривали ли вы, как пользователь массива узнает, какой тип Tile вы создали в каком индексе?Считаете ли вы, что все объекты в массиве должны иметь одинаковые размеры и требования к выравниванию?Ни одно из этих соображений не может быть применено компилятором.

Если вы заранее знаете список возможных типов плиток, я предлагаю использовать:

// let Tile1, Tile2, Tile3 be the possible types
std::vector<std::variant<Tile1,Tile2,Tile3>>

Если вы не можете ограничить список типов,тогда вы можете использовать:

std::vector<std::any>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...