我有一个关于在C ++中释放内存的问题:
typedef struct type1
{
int a;
int b;
float c;
} Type1;
typedef struct type2
{
int a;
int b;
} Type2;
void *p = new Type1;
delete (Type2 *)p;
在这种情况下,即使p
被转换为不同大小的类型,p
指向的内存区域也会被完全删除吗?
答案 0 :(得分:14)
行为未定义。在这种情况下,只能通过类型为Type1
的指针删除动态分配的对象。
首先,通过使用(Type2 *)p
表达式中delete
获得的指针,您违反了别名规则。有一组有限的类型,可以使用p
指向的对象。可以找到C ++ 03中的规则in an answer to another question。 C ++ 11规则是相似的(差异与您的问题的答案无关)。
即使程序没有违反严格的别名规则,也会违反delete
表达式的要求。规范陈述(C ++11§5.3.5[expr.delete] / 3):
如果要删除的对象的静态类型与其动态类型不同,则静态类型应为要删除的对象的动态类型的基类,并且 静态类型应具有虚拟析构函数或行为未定义。
在delete
表达式中,对象的静态类型为Type2
,而动态类型为Type1
。类型不同,但静态类型不是动态类型的基类。
答案 1 :(得分:4)
这将是一个非常糟糕的主意,因为您要求编译器安排在Type1指针上运行Type2 :: ~Type2,并且该析构函数可能引用该对象的末尾。
在传统环境中,最终释放内存是正常的,因为operator delete
调用free
,它不关心你在编译时调用它的类型。然而,在不那么传统的环境中,它可能是一场灾难。
答案 2 :(得分:0)
虽然James已完全回答了这个问题,但我想指出一件事:
在适当的C ++代码中,您几乎永远不会使用void
指针,这意味着您的代码可能如下所示:
SubType *p = new SubType;
BaseType* pB = (BaseType*)p;
delete pB;
在这种情况下,即使BaseType
具有正确的虚拟构造函数,如果SubType
不是从BaseType
派生的,也可能存在未定义的行为。在这里,普通的C风格演员阵容不是很幸运。
但是如果你使用dynamic_cast
,如果p
没有指向多态类型的对象,编译器很可能不会让你这样做。即使p
指向多态类型的对象,但BaseType
不是SubType
的基类型,dynamic_cast
也会返回NULL
而你可以适当地处理这个状态:
SubType *p = new SubType;
BaseType* safePtr = dynamic_cast<BaseType *>(p);
if (!safePtr) // p doesn't point to object of type derived from BaseType
... // handle this situation
else // p points to object of polymorphic type derived from BaseType
delete safePtr;