2014-06-18 56 views
1

我正在開發一個帶有插件系統的程序,它允許用戶以DLL文件的形式開發自己的模塊。 模塊應該使用在由應用程序的所有組件導入的DLL中定義的對象。下面是一個示例對象將是什麼樣子:導出構造函數的風險造成堆損壞

#include <boost/system/api_config.hpp> 

#if defined BOOST_WINDOWS_API 
    #ifdef EXPORT 
     #define API __declspec(dllexport) 
    #else 
     #define API __declspec(dllimport) 
    #endif 
#else 
    #define API 
#endif 

class A 
{ 
public: 
    API A(); 
    API virtual ~A(); 
}; 

所有DLL靜態建(用自己的CRT),並具有完全相同的編譯標誌。我知道通過DLL邊界交換對象可能會變得毛茸茸的,所以我幾乎在任何地方都使用boost::shared_ptr。圍繞對象構造函數有一個困難:如果我在堆棧上創建對象(來自不同的DLL),則所有事情都按預期工作。但是,如果我使用運算符new,則會刪除該對象時堆損壞。

A a; // Works fine, no problem when the object goes out of scope. 

A* b = new A(); 
delete b; // Causes heap corruption! 

這是什麼方法?如果我必須在對象的DLL中定義一個方法,例如A* A::create() { return new A(); },我覺得代碼的可讀性會降低。在最糟糕的情況下,我想讓new運營商保持私密,以確保用戶不會使用它。

+0

我不知道我得到您的問題。從你展示的內容來看,你的DLL裏沒有任何東西,因爲所有的函數都是「inline」的。你通常不需要在內聯函數或模板上使用'__declspec'。 –

+0

如果我更改代碼,以便構造函數和析構函數不是內聯的,它對我來說很有用。您確定您爲DLL和使用它的代碼都指定了兼容的鏈接器選項嗎? (特別是,你可能需要系統地使用'/ MD'或'/ MDd',沒有它,你會看到這樣的問題。) –

+0

我的構造函數/析構函數實際上不是內聯函數,我編輯了文章以反映它。我確認我得到了堆腐敗。我的構建標誌確實是'/ MT'和'/ MTD',但我不想改變它們。 – executifs

回答

2

根據您responsed到我的評論:/MT/MTd不要」 t 可以很好地處理DLL(即使它們是默認設置)。如果你想 使用DLL和動態分配,你必須使用/MD/MDd。當您使用/MT/MTd時,您可以有效地告知 系統爲每個DLL使用單獨的堆。這意味着 分配在一個,並在另一個刪除將損壞 堆。並且當析構函數是虛擬的時,實際的delete 將在delete表達式中的析構函數中,而不是。 (實際的問題是與mallocfree,由 operator new()operator delete()函數調用。)

解決此工作的傳統方法是使用工廠 方法用於動態分配和靜態或成員函數 刪除。一個替代方案(未嘗試,但我認爲它將 工作)是定義非內聯operator new()operator delete()成員,它們只是轉發到mallocfree。 (在operator new的情況下,當然,你必須檢查 您從malloc得到的指針不爲空,並拋出一個 std::bad_alloc如果它是。)

但這些都只是變通的 不存在的情況。一切工作正常與/MD/MDd,這是 你應該使用什麼(即使這意味着你不能合法地 在沒有許可證 視頻工作室的機器上調試版本)。

1

我認爲這個問題可能與您正在混合堆的事實有關。當你用靜態鏈接的所有CRT構建你的DLL時,意味着它維護着自己的堆。但是主機進程中的命令newdelete正在使用進程堆。我認爲這種配置可能會導致問題。我認爲最好的辦法是兩個方法添加到您的DLL:CreateA()DestroyA(),並且只使用它們堆對象的分配/銷燬從DLL A.

0

解決方法是從不從DLL中導出構造函數。改爲導出工廠功能。從不輸出具體課程。僅導出純粹的抽象類。無論如何,這是一個很好的風格,減少耦合和所有相關的好東西。