比方說,我有以下代碼:如何清理資源
class BaseMember
{
};
class DerivedMember : public BaseMember
{
};
class Base
{
private:
BaseMember* mpMember;
protected:
virtual BaseMember* initializeMember(void)
{
return new BaseMember[1];
}
virtual void cleanupMember(BaseMember* pMember)
{
delete[] pMember;
}
public:
Base(void)
: mpMember(NULL)
{
}
virtual ~Base(void)
{
cleanupMember(mpMember);
}
BaseMember* getMember(void)
{
if(!mpMember)
mpMember = initializeMember();
return mpMember;
}
};
class Derived : public Base
{
protected:
virtual BaseMember* initializeMember(void)
{
return new DerivedMember;
}
virtual void cleanupMember(BaseMember* pMember)
{
delete pMember;
}
};
基地和BaseMember是API的一部分,並可以通過API的用戶被繼承,就像它通過示例代碼中的Derived和DerivedMember完成的一樣。
Base通過調用它的虛擬工廠函數initializeMember()來初始化mpBaseMember,以便派生類可以重寫工廠函數以返回DerivedMember實例,而不是BaseMember實例。
但是,當從基類構造函數中調用虛函數時,調用基本實現而不是派生類覆蓋。 因此,我正在等待mpMember的初始化,直到它第一次被訪問(這當然意味着基類和任何派生類,可能會進一步得到它本身,不允許從內部訪問該成員構造函數)。
現在的問題是:調用基本析構函數中的虛擬成員函數將導致該函數的基類實現調用,而不是派生類覆蓋。 這意味着我不能簡單地從基類析構函數中調用cleanupMember(),因爲它會將其稱爲基類實現,它可能無法正確地清理東西,initializeMember()的派生實現已初始化。 例如,基類和派生類可以使用不兼容的分配器,這些分配器在混合時可能會導致未定義的行爲(如在示例代碼中 - 派生類通過new分配成員,但基類使用delete []來釋放它)。
所以我的問題是,我該如何解決這個問題? 我想到的是: a)API的用戶必須在Derived實例被破壞之前顯式調用某些清理函數。這可能會被遺忘。 b)(大部分)派生類的析構函數必須調用清理函數來清理初始化由基類觸發的東西。由於所有權責任被混淆,基本類觸發器分配,但派生類必須觸發釋放,這是非常不直觀的,並且派生類的作者無法知道,除非他讀取API文檔足以找到這些信息。 我真的希望以比使用用戶內存或他的可靠性來徹底閱讀文檔更加不可靠的方式來做到這一點。
有沒有其他方法?
注意:由於派生類可能不存在於基類的編譯時,所以靜態多態不是此處的選項。
工作解決方案值得一提的明確規定,你不能調用基類的析構函數派生類的功能派生類將已經被破壞。 – Bathsheba
您的實施違反了「資源獲取初始化」習慣用法。我寧願問一下在基本或派生構造函數中如何分配內存(+1)。 – cpp
智能指針有什麼問題? – doctorlove