我正在将一些代码移植到Windows并且难以接受。有一些代码在启动时自动运行以将指针复制到指针,并在退出时再次运行以删除指向指针的指针(如果它不为空。)
我创建了一个示例程序来重现行为
int main()
{
// Pointer to a Pointer, current crash.
InterfaceClass** ptrptr;
ConcreteTwo* object = new ConcreteTwo();
ptrptr = (InterfaceClass**)(&object); // cast is required for some reason.
delete *ptrptr; // Crash here.
// Single pointer, works fine.
InterfaceClass* ptrptr;
ConcreteTwo* object = new ConcreteTwo();
ptrptr = object;
delete ptrptr;
// There are other cases where there are only 3 classes in the hierarchy.
// This also works fine.
InterfaceClass** ptrptr;
ConcreteOne* object = new ConcreteOne();
ptrptr = (InterfaceClass**)(&object);
delete *ptrptr;
return 0;
}
类层次结构如下所示。基类是一个带有一些纯虚函数的接口,并且被整个程序中的许多类包含,使得许多对象可能从多个地方继承它。因此,具体实现必须使用“public virtual InterfaceClass”扩展它。在此示例中,删除“虚拟”可解决崩溃。
class InterfaceClass {
public:
virtual ~InterfaceClass() {};
InterfaceClass() {}
};
class ConcreteClass : public virtual InterfaceClass {
public:
ConcreteClass() { }
virtual ~ConcreteClass() {}
};
class ConcreteOne : public ConcreteClass
{
public:
ConcreteOne(void) {}
virtual ~ConcreteOne(void) {}
};
class ConcreteTwo : public ConcreteOne
{
public:
ConcreteTwo(void) {}
virtual ~ConcreteTwo(void) {}
};
答案 0 :(得分:5)
那么你是否熟悉指针的类型与它指向的类型几乎没有任何关系?换句话说,如果你认为T1从T2继承T1 *也继承自T2 *?那是错的。现在,这对您目前的情况有何影响?
InterfaceClass** ptrptr;
ConcreteTwo* object = new ConcreteTwo();
ptrptr = (InterfaceClass**)(&object); // cast is required for some reason.
这是C风格转换的主要问题。好的,所以它节省了一些水平空间,但你甚至知道你刚刚做了什么样的演员表?这不是你的想法。您实际上从ConcreteTwo *类型执行了 reintpret_cast 到一个不相关的类型InterfaceClass *!现在指针地址与你说的类型无关。
然后你将一个重新解释的指针类型抛入删除状态,这会立即导致你违反自己的括约肌。
答案 1 :(得分:3)
好吧,编译器警告过你,你决定按照自己的方式去做......
你不能做这个演员:
ptrptr = (InterfaceClass**)(&object);
因为object
指向ConcreteTwo
,这与InterfaceClass
不同。 InterfaceClass
的{{1}}子对象位于不同的地址。 ConcreteTwo
不是指向*ptrptr
实例的指针。
传递给InterfaceClass
的指针是指向delete
的指针,但是你向编译器说它是指向ConcreteTwo
的指针。 InterfaceClass
假设它确实是delete
,因此崩溃。
答案 2 :(得分:2)
答案 3 :(得分:1)
我认为问题出现在铸造线上。顺便说一句,如果您删除插入的强制转换,编译器会准确地告诉您这是什么问题。
如果你真的想这样做,我强烈建议你这样做,你应该首先创建一个临时的:
ConcreteTwo* object = new ConcreteTwo();
InterfaceClass* ptr = object;
然后您可以获取其地址并将其分配给ptrptr
变量:
InterfaceClass** ptrptr = &ptr;
现在你可以安全地删除它:
delete *ptrptr;
考虑到如果ptr
超出ptrptr
之前的范围,则删除可能会再次崩溃。
至于其余部分,Noah解释了为什么你的代码无效。