2

Non-virtual Interface 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()as protectedin的访问权限Object,这似乎是一个丑陋的解决方案,因为它会Object大大削弱 的封装。

当然,您仍然可以覆盖v_load()以调用新函数v_pawnLoad(),然后由客户端覆盖,但这似乎很容易出错,因为许多客户端可能会重载错误的函数。

那么,我如何设计Pawn这样一种方式,即客户端仍然可以覆盖v_load(),同时保持检查前置条件或调用其他函数的能力,并且(如果可能)不启用,更不用说要求客户端ObjectPawn调用基本v_load()实现了?

4

2 回答 2

2
  • 如果您的意图是允许人们“扩展”而不是“替换”load的行为,那么将您当前拥有的代码放入其中,v_load然后load最后调用一个空v_load的。
  • 或者,v_load protected如果您想让人们在“替换”或“扩展”之间进行选择,您也可以制作。
  • 如果您只想让他们替换行为,那么您的代码就可以了。

作为奖励,在所有这 3 个变体中,v_load如果您没有默认行为,您可以通过将您的纯虚拟设置为“强制”来更改“允许”。

如果您希望将覆盖限制为您的Pawn子类,请将final关键字添加到v_loadinPawn并使用另一个虚拟函数来允许子类Pawn自定义其行为。

于 2014-08-21T15:02:24.463 回答
0

在一些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上输出。

于 2014-08-21T15:13:06.593 回答