假设我有以下代码:
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;
}
};
Base 和 BaseMember 是 API 的一部分,可以由该 API 的用户子类化,就像在示例代码中通过 Derived 和 DerivedMember 完成的那样。
Base 通过调用其虚拟工厂函数 initializeMember() 来初始化 mpBaseMember,以便派生类可以覆盖工厂函数以返回 DerivedMember 实例而不是 BaseMember 实例。
但是,当从基类构造函数中调用虚函数时,将调用基类实现而不是派生类覆盖。 因此,我正在等待 mpMember 的初始化,直到它第一次被访问(这当然意味着,基类和任何可能进一步派生的派生类不允许从内部访问该成员构造函数)。
现在的问题是:从基类析构函数中调用虚拟成员函数将导致调用该函数的基类实现,而不是派生类覆盖。 这意味着我不能简单地从基类析构函数中调用 cleanupMember() ,因为那样会调用它的基类实现,这可能无法正确清理 initializeMember() 的派生实现已经初始化的东西。 例如,基类和派生类可以使用不兼容的分配器,这可能会在混合时导致未定义的行为(如示例代码中 - 派生类通过 new 分配成员,但基类使用 delete[] 来释放它) .
所以我的问题是,我该如何解决这个问题? 我想到的是: a) API 的用户必须在 Derived 实例被销毁之前显式调用一些清理函数。这很可能会被遗忘。 b)(大多数)派生类的析构函数必须调用清理函数来清理基类触发初始化的东西。这感觉很丑陋,设计也不好,因为所有权责任混淆了:基类触发分配,但派生类必须触发释放,这是非常违反直觉的,派生类的作者除非阅读API 文档足够彻底,可以找到该信息。 我真的很想以一种更可靠的方式来做到这一点,而不是依靠用户的内存或他的可靠性来彻底阅读文档。
是否有任何替代方法?
注意:由于派生类在基类的编译时可能不存在,静态多态性在这里不是一个选项。
最佳答案
如何修改包含清除方法的工厂模式?意思是,添加一个属性,如 memberFactory
,一个类的实例,提供创建、清理以及对成员的访问。虚拟初始化方法将提供并初始化正确的工厂,析构函数~Base
将调用工厂的清理方法并销毁它。
(嗯,这与工厂模式相去甚远......也许它有另一个名字?)
关于c++ - 如何清理虚拟成员函数分配的资源,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19033044/