非虚拟接口(interface)惯用语 (NVI) 非常不言自明:您不编写 public virtual
函数,而是编写调用 private 的
实现函数,如下所示:public
函数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()
的访问权限指定为 Object
中的 protected
,这看起来像是一个丑陋的解决方案,因为它会大大削弱 Object
的封装性。
您当然仍然可以重写v_load()
来调用新函数v_pawnLoad()
,然后由客户端重写,但这似乎很容易出错,因为很多客户端可能会重载错误的函数。
那么,我如何设计Pawn
,让客户端仍然可以重写v_load()
,同时保持检查先决条件或调用其他函数的能力, (如果可能)不启用,更不用说要求 Object
或 Pawn
的客户端调用基本 v_load()
实现?
最佳答案
- 如果您的目的是允许人们“扩展”而不是“替换”
load
的行为,那么请将您当前在v_load
中的代码放入load
然后最后调用一个空的v_load
。 - 或者,如果您想让人们在“替换”或“扩展”之间进行选择,您可以将
v_load
设置为protected
。 - 如果您只是想让它们替换行为,那么您的代码就可以了。
作为奖励,在所有这 3 个变体中,如果您没有默认行为,您可以将 v_load
设为纯虚拟,从而将“allow”更改为“force”。
如果您希望限制对 Pawn
子类的覆盖,请将 final
关键字添加到 Pawn
中的 v_load
code> 并使用另一个虚拟函数来允许 Pawn
的子级自定义其行为。
关于c++ - 将非虚拟接口(interface)和多级继承结合在一起,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25429148/