我尝试了一些有趣的代码(至少对我而言!)。在这里。
#include <iostream>
struct myStruct{
int one;
/*Destructor: Program crashes if the below code uncommented*/
/*
~myStruct(){
std::cout<<"des\n";
}
*/
};
struct finalStruct {
int noOfChars;
int noOfStructs;
union {
myStruct *structPtr;
char *charPtr;
}U;
};
int main(){
finalStruct obj;
obj.noOfChars = 2;
obj.noOfStructs = 1;
int bytesToAllocate = sizeof(char)*obj.noOfChars
+ sizeof(myStruct)*obj.noOfStructs;
obj.U.charPtr = new char[bytesToAllocate];
/*Now both the pointers charPtr and structPtr points to same location*/
delete []obj.U.structPtr;
}
我已将内存分配给charPtr并使用structPtr删除。当我向myStruct添加析构函数时崩溃,否则没有问题。
这里究竟发生了什么。据我所知,delete []将调用析构函数的次数与new []中给出的次数相同。为什么在myStruct中没有析构函数时它不会崩溃?
答案 0 :(得分:8)
首先,存储一个union的一个成员,然后以你正在做的方式读取另一个成员是Undefined Behavior,简单明了。这是错的,任何事情都可能发生。
除此之外,你尝试使用联盟的类型双关语很可能实际上有效(但记住不保证)。如果是这种情况,会发生以下情况:
您分配bytesToAllocate
类型char
对象的数组,并将该地址存储在工会指针中。
然后,在联合指针上调用delete[]
,键入为myStruct*
。这意味着它假定它是一个myStruct
个对象的数组,它将在每个对象上调用析构函数。但是,该数组不包含任何myStruct
个对象,它包含char
个对象。实际上,数组的字节大小甚至不是myStruct
大小的倍数!必须彻底混淆delete
实施。它可能将第一个sizeof(myStruct)
字节解释为一个myStruct
对象,并在这些字节中调用析构函数。然后,还剩下少于sizeof(myStruct)
个字节,但仍有一些剩余,所以析构函数会在那些不完整的字节上调用,超出数组,然后就会出现欢闹。
当然,由于这只是UB,我对上述行为的猜测可能会有所不同。简单明了,你已经把它弄糊涂了,所以它很混乱。
答案 1 :(得分:2)
delete做了两件事,调用析构函数并释放内存。
您为一种类型分配数据,但如果伪造另一种类型则删除。 你不应该这样做。在C / C ++中可以做很多事情,看一下IOCCC以获得更多灵感: - )
没有任何功能且只有plain old data的C ++结构本身就是一个POD。它在创建/删除时从不调用构造函数/析构函数。 甚至不是标准的c-tors / d-tors。仅出于性能原因。
具有(EDIT)用户定义的复制赋值运算符,虚拟函数或d-tor的结构在内部稍微复杂一些。它有一个成员函数指针表。
如果使用chars分配内存块,则不会初始化此表。当您尝试使用非POD类型删除此内存块时,它首先调用析构函数。并且由于析构函数指针未初始化,它会调用内存空间中的任何内存块,认为它是函数。这就是崩溃的原因。
答案 2 :(得分:2)
它的工作原理是因为myStruct没有析构函数。 [编辑:我现在看到你试过了,它确实崩溃了。我会发现这个问题很有趣,为什么它会与那个dtor崩溃,因为dtor不能访问该对象的任何内存。]
正如其他人所说,free []的第二个功能,除了可能调用元素的dtors(这里没有发生,如上所述)是释放内存。
这在您的实现中非常有效,因为通常免费的存储实现只是为此目的分配一块内存,其大小保存在该内存中的簿记位置。大小(一旦分配)是类型无关的,即不是从free上的指针类型派生的。参看How does delete[] "know" the size of the operand array?。 malloc like,类型不可知分配器返回内存块并且很高兴。
请注意,当然,你所做的是夸张的,不要在家里这样做,不要发布它,并使ppl签署非责任分歧,不要在核设施中使用它,并始终从主要返回int ()。
答案 3 :(得分:1)
问题是obj.U.structPtr
指向一个结构,它可以有一个构造函数和析构函数。
delete
也需要正确的类型,否则无法调用析构函数。
因此,使用char
创建new
数组并将其删除为struct
指针是违法的。
如果您使用malloc
和free
,那就没关系。这不会调用构造函数和析构函数。