非虚拟接口(interface)惯用语(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
,当然会导致无限循环。此外,要求他们这样做可能会在他们忘记该 call 时导致错误。如果我希望他们启用它,则必须将v_load()
的访问权限指定为protected
中的Object
,这似乎是一个丑陋的解决方案,因为它会大大削弱Object
的封装。当然,您仍然可以重写
v_load()
来调用新函数v_pawnLoad()
,该新函数随后将被客户端覆盖,但这似乎很容易出错,因为许多客户端可能会重载错误的函数。因此,如何设计
Pawn
,使客户端仍然可以覆盖v_load()
,同时保持检查前提条件或调用其他功能的能力,并且(如果可能)不启用,更不用说要求Object
或Pawn
的客户端调用基础了v_load()
实现? 最佳答案
load
的行为,则将您当前在v_load
中拥有的代码放入load
中,然后最后调用一个空的v_load
。 v_load
成为protected
。 另外,在所有这三种变体中,如果没有默认行为,则可以将
v_load
设为纯虚拟,从而将“allow”更改为“force”。如果希望将替代项限制为
Pawn
子类,则将final
关键字添加到v_load
中的Pawn
中,并使用另一个虚拟函数允许Pawn
的子项自定义其行为。