2011-01-10 72 views
0

我的gui應用程序支持多態時間事件,這意味着用戶調用new,並且gui調用delete。如果運行時間不兼容,這可能會造成問題。這會導致與DLL不同運行時的問題嗎?

所以有人告訴我提議的解決方案將是這樣:

class base; 

class Deallocator { 
    void operator()(base* ptr) 
    { 
     delete ptr; 
    } 
} 

class base { 
public: 
base(Deallocator dealloc) 
{ 
    m_deleteFunc = dealloc; 
} 
~base() 
{ 
    m_deleteFunc(this); 
} 

private: 
Deallocator m_deleteFunc; 
} 

int main 
{ 
    Deallocator deletefunc; 

    base baseObj(deletefunc); 
} 

雖然這是一個很好的解決方案,它要求用戶創建,我不希望有一個釋放器對象。然而我想知道是否我爲每個派生類提供了釋放器:例如

class derived : public base 
{ 
    Deallocator dealloc; 
public: 
    Derived() : base(dealloc); 
{ 
} 
}; 

我認爲這仍然不起作用。約束是: addTimedEvent()函數是Widget類的一部分,該類也在dll中,但它由用戶實例化。另一個約束是一些派生自Widget的類使用它們自己的定時事件類來調用這個函數。

鑑於「誰叫新必須調用刪除」什麼可以工作給這些限制?

感謝

+0

您必須使基類析構函數爲虛擬。 – 2011-01-10 16:09:23

+0

@Hans:對於問題中的提議(斷開)機制,是的,析構函數應該是虛擬的。但許多處理問題的衆所周知的方法並不需要虛擬銷燬。 – 2011-01-10 16:14:33

回答

1

我建議你學習的COM引用計數模式(AddRef和Release)。這允許更靈活的生命週期並保證使用正確的釋放器,因爲對象會自行刪除。

請注意,如果您跨DLL邊界共享類對象,那麼您可能會遇到更大的問題,即使用相同的分配器。整個單義定義規則可以說明問題,並且在編譯器之間調用約定,數據佈局和名稱修改方案有所不同。所以如果你想要一個可重用的庫,你真的需要採用COM的方式來處理引用計數,自我刪除和只包含純虛函數的接口。無論您構建真正的COM對象還是您自己的COM類系統,都將取決於您的其他要求。

0

首先想到的是給基類虛擬(抽象?)SelfDestruct方法。假設你的DLL的消費者通過他自己創建的類,他將知道如何釋放它。

如果他能通過你寫的課程,那麼你就會遇到更多問題。我建議不要分配這樣的類,並提供一個靜態方法來用你自己的分配器來分配它們。

我不確定我是否已經非常清楚地解釋了我的想法......如果沒有,請問,我稍後會提供代碼。

0

可以使用給定約束的是,您將刪除函數指針與每個TimedEvent相關聯,其中兩個指針均被指定爲參數爲addTimedEvent
爲了減輕客戶端創建自定義刪除函數的負擔,您可以將作爲匿名命名空間的成員在小部件類的標頭中提供內聯刪除函數。

例如:

// Widget header 

class base; 
namespace { 
    inline void default_deleter(base* p) 
    { 
    delete p; 
    } 
} 

class Widget 
{ 
public: 
    addTimedEvent(base* event, void(*deleter)(base*)); 
}; 

內聯函數的優點是,它會在客戶端代碼的上下文中進行編譯,所以delete還將使用兼容的釋放器爲用於分配該事件的客戶端。

編輯:使deleter函數成爲匿名命名空間的成員。這是避免ODR違規所必需的。
如果沒有命名空間,您會得到兩個函數default_deleter,它們具有相同的外部名稱(因此它們與鏈接器相同),但具有不同的語義,因爲它們引用了不同的釋放器。
使用匿名命名空間,default_deleter的所有實例都將成爲鏈接器的獨立實體。這具有(不幸的)副作用,您不能再使用該函數作爲addTimedEvent的默認參數。

相關問題