C++ C++;:NVI和模板方法模式之间的差异?

C++ C++;:NVI和模板方法模式之间的差异?,c++,design-patterns,template-method-pattern,non-virtual-interface,C++,Design Patterns,Template Method Pattern,Non Virtual Interface,NVI()和模式之间的区别是什么 它们看起来非常相似,我读到它们基本上是一样的,但它们有细微的不同,因为模板更一般。NVI是一种习惯用法,模板方法是一种模式。NVI是一种利用C++动态调度的模板方法模式的实现;也可以使用模板元编程来在C++中创建模板方法来消除动态调度。p> 模式比惯用语更一般,语言可能会使用不同的惯用语来实现模式。如前所述,NVI是一种编程惯用语,与一类语言相关。赫伯·萨特(Herb Sutter)和其他人都在推广它,因为它有助于执行合同: 类不变量 函数契约(对传递的参数和

NVI()和模式之间的区别是什么


它们看起来非常相似,我读到它们基本上是一样的,但它们有细微的不同,因为模板更一般。

NVI是一种习惯用法,模板方法是一种模式。NVI是一种利用C++动态调度的模板方法模式的实现;也可以使用模板元编程来在C++中创建模板方法来消除动态调度。p>
模式比惯用语更一般,语言可能会使用不同的惯用语来实现模式。

如前所述,NVI是一种编程惯用语,与一类语言相关。赫伯·萨特(Herb Sutter)和其他人都在推广它,因为它有助于执行合同:

  • 类不变量
  • 函数契约(对传递的参数和生成的返回值的断言)
  • 重复操作(如日志记录)
  • 对生成的异常的控制(尽管这是个坏主意;))
然而,实现可能实际上有很大不同,例如,NVI实现的另一个示例是将其与Pimpl结合:

class FooImpl;

class Foo
{
public:
  enum type { Type1, Type2 };

  Foo(type t, int i, int j);

  int GetResult() const;

private:
  FooImpl* mImpl;
};
至于实施方面:

struct FooImpl
{
  virtual ~FooImpl();
  virtual int GetResult() const;
};

class FooType1: public FooImpl
{
public:
  FooType1(int i, int j);
  virtual int GetResult() const;
private:
  /// ...
};
我总是发现它更好地表达了这一点。你明白了吗

主要的一点是,
virtual
是一个实现细节。在接口中公开实现细节是个坏主意,因为您可能希望更改它们

此外,实现细节往往会影响二进制兼容性。例如,在类中添加一个新的
virtual
方法可能会改变虚拟表的布局(通用实现技术),从而破坏二进制兼容性。在gcc上,如果希望保持兼容性,则需要确保最后添加它(在虚拟机中)

通过使用上面的NVI+Pimpl组合,在公开的类中根本没有
virtual
(甚至没有private)。内存布局向后和向前兼容。我们已经实现了二进制兼容性

在这里,我们同时使用几种模式:

  • 模板法
  • 策略(因为我们可以随意交换指针)
  • 工厂(决定我们得到的实现)

我不会将wikipedia链接用作参考,他们对锁的使用在例外情况下是脆弱的…+1用于“虚拟is实现细节”和ABI考虑。+1。但是,我不同意您为虚拟接口创建单独的类似pimpl的类。最明显的缺点是它将所需的类数量增加了一倍。此外,如果FooImpl不是不透明的,它可能会吸引人们直接使用它,而跳过Foo。然而,它不太可能是不透明的,因为NVI的目的是允许人们覆盖虚拟实现,所以FooImpl必须是可公开访问的。尽管它有自己的缺点,我认为在一个类中实现NVI并避免同时使用公共虚拟函数是一种更好的方法。这也是一种更容易实施的策略:没有公共虚拟函数,而不是为提供公共虚拟接口的pimpl类类提供特殊情况。拥有
虚拟
函数的问题并不严重由于易于使用,它与ABI。
FooImpl
不透明的优点是,您将其限制在二进制文件中,因此在向客户交付新版本的二进制文件时不会出现ABI问题。很好,但是NVI与模板方法有何不同?似乎你是说NVI是在某种语言中实现模板方法的一种特定方式?那么你是说NVI基本上是模板方法模式的一种特定语言实现,除此之外没有什么真正的区别?如何使用C++模板来达到同样的结果?@罗伯特·巴尼斯。据我所见,没有一种明显的方法将C++模板作为模板方法。模板方法说,“这样做,然后做其他事情”,同时你可以为一个或另一个事情创建一个函子,与C++模板给你的类型参数没有真正的关系。我猜我仍然不明白你在这里的意思:“也可以使用模板元编程来在C++中创建模板方法来消除动态调度。“通常在模板方法中,有一些非虚拟调用和一些虚拟调用。您可以将template方法放在模板中,或针对模板专门化进行调用,以便在编译时解析调用,但是您不能直接创建模板专门化,这是一种模板方法-您可以从模板方法调用模板专门化,但模板专门化本身不是模板方法。e、 g.
模板T fma(ta,tb,tc){返回a+b*c;}
将调用T::operator+的正确专门化或重载。