C++ 为什么我的;这";成员函数内的指针为null?
我不想给你看一本书,所以我要简化一下。 我有一个名为“Tile”的类,它看起来类似于:C++ 为什么我的;这";成员函数内的指针为null?,c++,class,pointers,null,undefined-behavior,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 pu
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;
}
};
我想在一个大数组中分配它们中的许多,这个数组永远不会再移动(不需要移动),所以我手动进行分配
//Manual allocation to prevent constructor from being called
Tile* tiles = (Tile*)malloc(numTiles*sizeof(Tile));
if(tiles == nullptr) throw std::bad_alloc();
由于平铺类型一开始是未知的,所以我无法调用new来实现这一点。
现在我已经分配了内存,但是没有调用构造函数。
“世界生成器”运行并调用每个磁贴
tiles[tileIndex] = Tile(tileTypePtr);
现在它应该已经创建了所有类型的所有对象。
如果我渲染场景而不调用getNorthEast()代码>我可以看到,由于uvIndex
(仅指定要渲染的纹理部分),类型已正确设置。因此tt_u设置正确,构造函数也必须正确运行。
然而如果我现在调用getNorthEast()
printf语句告诉我this
指针是00000000。这会扰乱计算并导致崩溃
这可能是由于未定义的行为,但我看不出我做错了什么。。。我需要你的帮助
编辑1:
所以我现在有一个新的位置看。
我已将分配和分配更改为:
//allocation
tiles = (Tile*)new char[numTiles*sizeof(Tile)];
//assignment
new (&tiles[tileIndex]) Tile(tileTypePtr);
但是此
仍然是空PTR。这一次,赋值应该是完全覆盖,没有未定义的行为。分配的作用与std::vector.reserve()基本相同
附加信息:在主循环中调用getNorthEast()要晚得多。现在只是初始化。因此,它应该不会影响对象的构造与否
编辑2:
我刚刚重新尝试使用向量。同样的结果
std::vector<Tile> tiles;
//allocation
tiles_.reserve(numTiles);
//construction for each index
tiles.emplace_back(Tile(tileTypePtr));
std::向量块;
//分配
瓷砖储备量(单位);
//每个索引的构造
瓷砖。铺设背面(瓷砖(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”。您需要包含
标题才能使用它
但是,不需要自己重新实现这个缓冲区重用std::vector
除了负责危险内存管理以及异常安全和指针别名规则的微妙之处之外,还为您提供了这一功能。你已经被一条警告绊倒了。没有必要跳到下一个std::vector
通常是非默认可构造类型的动态数组的理想解决方案
编辑:下面的答案是基于我最初的解释,即您打算在数组中包含不同类型的对象。我现在注意到,您的对象中有一个Type
指针,并且您实际上可能只存储具有不同内部Type
指针的Tile
实例(我假设tt
应该是一个指针)
然而,您的结构似乎有点不稳定。您是否考虑过数组的用户如何知道您在哪个索引中构造了什么类型的Tile
?您是否考虑过阵列中的所有对象必须具有相同的大小和对齐要求?编译器无法强制执行这些注意事项
如果您事先知道可能的瓷砖类型列表,我建议使用:
// let Tile1, Tile2, Tile3 be the possible types
std::vector<std::variant<Tile1,Tile2,Tile3>>
//让Tile1、Tile2、Tile3成为可能的类型
向量
如果无法约束类型列表,则可以使用:
std::vector<std::any>
std::vector
malloc函数只分配内存,不调用构造函数!以任何方式使用未构造的对象(包括在调用对象赋值操作符时为其赋值)都会导致。请学习如何避免此类问题。tiles[tileIndex]=Tile(tileTypePtr)
错误,因为平铺[tileIndex]
使用未初始化的对象。您必须先调用构造函数,然后才能分配给它。@Someprogrammerdude“现在我分配了内存,但没有调用构造函数。”我很确定OP已经知道了这一点。//手动分配以防止调用构造函数
:为什么?这很奇怪。你应该解释一下。是的,我们需要一个。我们至少需要调用getNorthEast
的代码,我想把它们分配到一个永远不会移动的大数组中(不需要a),所以我手动分配——你知道吗?大多数(如果不是所有的话)std::vector的实现都做同样的事情,但是通过使用placement new
正确地执行。所以你在重新发明轮子,只是你的重新发明被打破了。好吧,我试过新的位置。我还将其添加到编辑中以显示其外观。本质上应该是一样的。但是此
仍然是空PTR。最初我使用的是一个向量(reserve()用于分配,emplace_back()用于构造),但由于出现了这个问题,我尝试将级别降低一点,以便更好地了解正在发生的情况。您关于tt_的假设是正确的。通过指针减法可以知道索引。事实:这就是“this”指针的用途unsigned int tileIndex=(unsigned int)(this-tiles)
。当我使用C时,它工作了(我把程序移到C++中以获得更好的功能)。我应该补充一点,tiles变量是全局变量,因此Tile可以访问它
std::vector<std::any>