c++ - 将非虚拟接口(interface)和多级继承结合在一起

标签 c++ inheritance c++11 encapsulation non-virtual-interface

非虚拟接口(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(),同时保持检查先决条件或调用其他函数的能力, (如果可能)不启用,更不用说要求 ObjectPawn 的客户端调用基本 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/

相关文章:

c++ - 用具有不同范围的均匀分布的随机数填充多维矩阵

Java 不明确的结果

c++ - 错误:请求从 ‘Mstream’转换为非标量类型 ‘std::string {aka std::basic_string<char>}’

C++11 普通旧对象默认值

java - 将父类(super class)实例传递给其构造函数是否是更好的做法?

c++11 - 枚举值需要多少位?

java - 为什么我们使用 if,else if 而不是多个 if block 如果主体是返回语句

c++ - 如何在函数中传递 const 变量?

c++ - 基准标记 - OpenCV - 特征检测和匹配

c++ - 对具有私有(private)可访问性的重载继承函数使用声明