非虚拟接口idiome(NVI)非常自我解释:您不会编写public virtual
函数,而是调用public
实现函数的private virtual
函数,例如这样:
class Object{
virtual void v_load();
public:
void load(){ v_load(); }
}
这使您(基类作者)能够检查并强制执行前后条件或应用其他函数,以便派生类的作者不会忘记它们。
现在,当您是派生作者时,您可能想要自己编写一个基类 - 让我们称之为Pawn
- 它扩展到load()
的功能,因此必须覆盖v_load()
。但现在你遇到了一个问题:
当您覆盖v_load()
时,其他想要从您的类派生的客户端将始终覆盖该行为,并且他们无法调用Pawn::v_load()
因为它是private
函数,他们可以拨打Pawn::load()
,因为它在{ v_load; }
中被定义为Object
,这当然会导致无限循环。此外,要求他们这样做可能会在忘记呼叫时导致错误。如果我希望他们启用它,我将必须在v_load()
中将protected
的访问权限指定为Object
,这似乎是一个丑陋的解决方案,因为它会削弱{{}的封装1}}非常。
你当然可以覆盖Object
来调用一个新函数v_load()
,然后被客户端覆盖,但这似乎非常容易出错,因为很多客户端可能会重载错误的函数
那么,我如何设计v_pawnLoad()
以使客户端仍然可以覆盖Pawn
,同时保持检查前置条件或调用其他函数以及(如果可能)不启用的能力,让单独要求v_load()
或Object
的客户调用基础Pawn
实施?
答案 0 :(得分:2)
load
的行为,那么请将v_load
中当前拥有的代码放入load
然后调用最后是空v_load
。v_load
protected
。作为奖励,在所有这三种变体中,如果您没有默认行为,则可以通过使v_load
成为纯虚拟来更改“允许”。
如果您希望将覆盖范围限制在Pawn
子级别,请将final
关键字添加到v_load
中的Pawn
,并使用其他虚拟函数来允许Pawn
的子级{1}}自定义其行为。
答案 1 :(得分:0)
mixin'怎么样?在一些CRTP?
#include <iostream>
class BaseObject
{
private:
virtual void v_load() = 0;
public:
void load() { v_load(); }
};
template<typename Derived>
class Object : public BaseObject
{
private:
virtual void v_load() { static_cast<Derived&>(*this).load(); }
};
class Pawn : public Object<Pawn>
{
public:
void load() { std::cout << "Pawn::load()" << std::endl; }
};
class BlackPawn : public Pawn
{
private:
virtual void v_load() {
std::cout << "BlackPawn::v_load()" << std::endl;
std::cout << "- "; Pawn::load();
}
public:
void load() {
std::cout << "BlackPawn::load()" << std::endl;
std::cout << "- "; Pawn::load();
}
};
class BigBlackPawn : public BlackPawn
{
private:
virtual void v_load() {
std::cout << "BigBlackPawn::v_load()" << std::endl;
std::cout << "- "; BlackPawn::load();
}
public:
void load() {
std::cout << "BigBlackPawn::load()" << std::endl;
std::cout << "- "; BlackPawn::load();
}
};
template<typename T>
void load(T& x)
{
x.load();
}
void vload(BaseObject& x)
{
x.load();
}
int main()
{
Pawn p;
BlackPawn bp;
BigBlackPawn bbp;
load(p);
load(bp);
load(bbp);
std::cout << std::endl;
vload(p);
vload(bp);
vload(bbp);
}
ideone上的输出。