2016-04-19 92 views
3

我承認這個問題聽起來很一般。但畢竟,從DLL中導出類是一個普遍和困難的話題,並且坦率地說,我目前困惑於一個相當普遍的層面。我可以組織DLL中的類嗎?

簡短問題:C++和DLL中的面向對象編程如何配合在一起?

長的問題:閱讀thisthis後,我有點失望和困惑,因爲我不知道如何面向對象編程可以用DLL的工作,如果DLL邊界不允許共享對象(假設兩個DLL使用了不同的編譯器或編譯器版本)。對於出口類的選項只有這些(如解釋herehere):

  • 出口創建和刪除方法(C風格,懸擺指針的危險,沒有對象作爲參數,醜)
  • 出口純虛類和一個工廠函數,它創建一個從純虛擬類派生的實際實現類的實例(需要繼承,刪除對象時需要刪除對象)

例如,我想將通用實用程序一個DLL中的類,然後在其他DLL中的幾個類中使用,它們本身在其他DLL中使用。我怎樣才能做到這一點?這是一個不明確的方式來組織我的課程?

獎勵問題:如果我導出一個具有指向實現的指針的類,這相當於導出純虛擬類和工廠函數嗎?或者導出的成員函數必須是虛擬的?

編輯:如果它很重要,我在使用Visual Studio 2010的Windows 7上。遷移vom較舊的Visual Studio讓我對此問題很敏感。

+0

使用DLLs是一個非常實用的解決方案,當您進行小的更改時,必須等待很長時間才能讓程序編譯和鏈接。使用它們作爲一個庫,應該可以被任何編譯器設置的任何編譯器版本使用,好吧,沒有。二進制兼容性不是C++功能,必須由您添加,您已經知道需要什麼。 –

+0

@Hans Passant:是否有替代DLL的方式來以可重用的方式組織源代碼?頭部實現可能?還要別的嗎? – Fabian

回答

2

TL; DR:這取決於。

的簡單情況

當兩個DLL(或DLL和可執行文件)已建成使用相同的編譯器和編譯器版本,以及他們使用運行時(調試或釋放)的相同的味道,和他們鏈接對運行時的動態版本,你可以做任何你想要的。例如,跨DLL邊界刪除。

的減簡單的情況

當對一個調試運行一個DLL鏈接和反對釋放運行的其他鏈接,事情變得更加複雜。這是因爲調試運行時具有不同的STL模板,用於調試目的。例如,您無法操作從發行版DLL中的調試DLL分配的std :: vector,反之亦然。只要你將這些STL模板限制在正確的DLL中,事情就應該起作用。顯然,如果您自己公開不同的ABI,例如通過聲明#ifndef NDEBUG區塊中的成員,您會遇到問題。

根據調試DLL使用的模板,您可能必須使用_CRT*定義的方法來強制執行某些操作。

您還應該創建和銷燬來自同一個DLL的對象。

可怕(又名真實世界)案例

當編譯器版本不匹配,當至少一個DLL與靜態鏈接的運行上。

你會遇到大量的ABI問題。唯一安全的做法是通過extern "C"(廣泛地)依賴C ABI。如果你在運行時加載DLL,這也可能是你想要做的。

一件好事,在這一點上做:

  • 寫您的DLL在C++
  • 請勿將使用C++ ABI(dllexport)任何東西。
  • 在你的C++代碼周圍寫一個C封裝器。
  • 公開此C API與__declspec(dllexport)
  • 圍繞公開的C API編寫一個僅頭文件C++包裝器。

然後客戶端將使用您的只包頭包裝器,因此每次調用您的DLL代碼都將使用C ABI,這非常穩定且不太可能中斷。

+0

簡單的情況是否現實?如果我有一整套我想從一個編譯器版本遷移到另一個編譯器的版本?人們在這種情況下做什麼?另外,你能否更詳細地解釋一下這個簡單的C包裝器和只包含頭文件的C++包裝器,可能只是一個小例子?那樣會很親切。 – Fabian

1

我寫了one of the questions you linked已經很長時間了,但讓我試着去幫忙。

簡短的問題:C++和DLL中的面向對象編程如何配合在一起?

這完全取決於您的物體正在曝光。如果你的對象返回純C值,你應該沒問題。如果您的對象返回包含plain-C值的類POD,那麼您應該也可以。我試圖在問題/答案中解決的頭痛問題幾乎完全與STL相關。

要回答自然後續問題,STL播放非常糟糕與DLL。由於不同的打包,成員重新排序等,C++類具有天生的交叉編譯器兼容性問題.STL增加了額外的潛在不兼容層,因爲當內置到調試DLL中時,可以添加額外的成員。

例如,我想將通用工具類放入一個DLL中,然後將其用於其他DLL中的幾個類中,這些DLL本身用於其他DLL中。我怎樣才能做到這一點?

在未能成功傳遞STL類型之後,我最終將我的C++類包裝到一個將它們轉換爲C語言對象的層中。在可能的情況下,我返回基本的數據類型。在哪裏我不得不分配內存(string,vector等)我最終以c風格的創建/刪除功能。我相信我仍然暴露出一個純粹的虛擬接口來保護儘可能多的實現細節;我只是沒有試圖直接通過DLL邊界傳遞任何STL對象。

如果我導出一個具有指向實現的指針的類,這相當於導出一個純虛擬類和一個工廠函數嗎?或者導出的成員函數必須是虛擬的?

如果我正確地理解了這個問題,那麼您想同時做兩件事。現在

struct MyDLL 
{ 
    virtual void DoSomething() = 0; 
    virtual int AddSomething(int argument1, int argument2) = 0; 
} 

extern "C" __declspec(dllexport) MyDLL* GetMyDLL(); 

來電者可以撥打GetMyDLL,並有一個指針類,您可以放心地執行幕後的虛函數,而不必擔心您的來電者看到你在做什麼。

+0

然後可以在POD結構中使用指向我的自定義類的指針嗎?指針是POD類型,對嗎?我的想法是讓'MyDLL'結構有一個指向內部類的指針,並讓'MyDLL'具有委託給內部類的普通函數。 – Fabian

+0

我的問題是針對人們如何生活這個嚴重的問題。我希望能夠在我的DLL周圍移動對象,並且無法安全地做到這一點。人們是否忽略了可能存在的問題,或者迫使他們的開發人員使用相同的編譯器,或者他們只是不分享除POD和整數之外的任何內容?也許我必須以同樣的方式改變我的編程方式。 – Fabian

+0

只要你實際上沒有通過EXE/DLL邊界傳遞一個C++類,我認爲你應該沒問題。我認爲你的「指向自定義類的指針」的功能很像我的答案中的「MyDLL」/「GetMyDLL」代碼?這應該足夠安全。但是,如果你開始試圖暴露指向STL對象的指針,那麼不要指望結束。 EXE的編譯器對於這些STL對象可能有不同的內存佈局,導致指針指向您的DLL和EXE之間的不同內容。 – computerfreaker

相關問題