比方说,我有以下代码:如何清理资源
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