2010-12-17 172 views
6

我正在将一些代码移植到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; 
} 

的类层次结构看起来是这样的。基类是一些带有一些纯虚函数的接口,并且被整个程序中的许多类所包含,使得许多对象可能从多个地方继承它。正因为如此,具体实现必须用“公共虚拟接口类”扩展它。在这个例子中,删除“虚拟”解决了崩溃。

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

回答

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 *!现在指针地址与你所说的类型无关。

然后你把一个重新解释的指针类型折腾成删除,这会立即导致你违反自己的括约肌。

3

那么,编译器警告过你,你决定做你的方式......

你不能这样做投:

ptrptr = (InterfaceClass**)(&object); 

因为objectConcreteTwo,这与InterfaceClass不一样。 InterfaceClassConcreteTwo的子对象位于不同的地址。 *ptrptr不是指向InterfaceClass的实例的指针。

您传递给delete的指针是指向ConcreteTwo的指针,但是您向编译器指出它是指向InterfaceClass的指针。 delete假设它确实是InterfaceClass,因此崩溃。

1

我认为问题出在铸造线上。顺便说一句,如果你删除了你插入的cast,编译器会准确地告诉你什么是问题。

如果你真的想这样做,这样,我强烈建议你,你应该先创建一个临时的:

ConcreteTwo* object = new ConcreteTwo(); 
InterfaceClass* ptr = object; 

,那么你可以对自己的地址,并将其分配给ptrptr变量:

InterfaceClass** ptrptr = &ptr; 

现在你可以放心地将其删除:

delete *ptrptr; 

考虑到如果ptr超出ptrptr之前的范围,删除可能会再次崩溃。

至于其余的,诺亚解释了为什么你的代码不工作。