为什么我的成员函数内的“ this”指针为空?

时间:2018-10-05 12:17:36

标签: c++ class pointers null undefined-behavior

我不想给你看一本书,所以我要简化一下。 我有一个名为“ Tile”的类,它看起来与此类似:

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 :(得分:4)

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);

这种将内存重用于新对象的语法称为“ placement new”。您需要包含<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>