2010-12-17 174 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之前的範圍,刪除可能會再次崩潰。

至於其餘的,諾亞解釋了爲什麼你的代碼不工作。