假设我有以下代码:
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 文档足够彻底,可以找到该信息。我真的很想以一种比依靠用户的记忆力或他的可靠性来彻底阅读文档更可靠的方式来做到这一点。
有没有其他方法?
注意:由于派生类在基类的编译时可能不存在,因此静态多态在这里不是一个选项。