2009-12-03 53 views
0

很抱歉,如果這個被問了,但我有一個很難尋找析構函數和訪問衝突=)基類虛析構函數訪問衝突

這裏的C++僞代碼的情景:


在DLL1 (編譯/ MT)

class A 
{ 
public: 
    virtual ~A()  <== if "virtual" is removed, everthing works OK 
    { 
    } 
} 

class B : public A 
{ 
public: 
    __declspec(dllexport) ~B() // i did try without exporting the destructor as well 
    { 
    }  <== Access Violation as it returns (if fails in assembly at delete operator...) 
} 

在DLL2可鏈接到DLL1

main()enter code here 
{ 
    B* b = new B(); 
    delete b;   <== Access Violation 
} 

這是怎麼回事?我有一個腦筋急轉彎嗎?如果我讓A的析構函數爲非虛擬的,那麼一切都可以正常工作 - 即使A和B的析構函數被調用(就好像A的析構函數是虛擬的 - 這是由於它是公共的事實嗎?)。

雖然我的主要問題是爲什麼當基類的析構函數被聲明爲虛擬時存在訪問衝突?

回答

0

因爲它在刪除操作符中崩潰,並且因爲您說您正在使用/MT編譯,所以我相信原因是您的兩個DLL不共享相同的堆:因爲它們都鏈接到靜態庫,他們每個都得到他們自己的運行時堆的私人副本;而且你有效地從一個堆中分配一個DLL中的內存,並從另一個堆中刪除另一個DLL中的內存。

要解決此問題,您可以聲明析構函數受到保護,並且不會導出它。相反,創建一個靜態銷燬功能:

class A 
{ 
public: 
    __declspec(dllexport) static void destroy(A* self) { delete self; } 
protected: 
    virtual ~A() {} 
}; 

int main() 
{ 
    B* b = new B(); 
    A::destroy(b); //instead of delete b 
    return 0; 
} 

或者,你可能會喜歡這個,因爲它不涉及改變的源代碼,確保兩個DLL是建立一個使用DLL版本使用同一個堆,即的C運行時間,我認爲這意味着使用/MD選項而不是/MT

+0

「你從一個堆中有效地分配一個DLL中的內存,並從另一個堆中刪除另一個DLL中的內存。「 我不認爲這是代碼的樣子。 – leiz 2009-12-03 04:19:10

+0

雖然這不是代碼的樣子,但我認爲這就是發生了什麼。可能是析構函數是否是虛擬的,會影響析構函數是否被內聯到調用它的DLL中。 – ChrisW 2009-12-03 04:29:52

+0

沒有在cpp中的析構函數而不是在頭中內聯也解決了這個問題? – 0xC0DEFACE 2009-12-03 04:37:08

1

謝謝你們!謝謝ChrisW ..看起來就是這樣發生的事情。 我需要添加唯一的另一件事是靜態分配(靜態createNew()):

class A 
{ 
public: 
    __declspec(dllexport) static void destroy(A* self) { delete self; } 
protected: 
    virtual ~A() {} 
}; 

class B : public A 
{ 
protected: 
    B(); 
public: 
    __declspec(dllexport) static B* createNew() { return new B(); } 
} 

int main() 
{ 
    B* b = B::createNew() 
    A::destroy(b); //instead of delete b 
    return 0; 
} 

(順便說一句與/ MD編譯不是一個選項,我給我的部署環境)