回答

11

NVI是一个成语,模板方法是一个模式。 NVI是在C++中使用动态分派的模板方法模式的实现;还可以使用模板元编程在C++中创建模板方法以消除动态分派。

模式是比成语更一般的,和语言可以使用不同的习语来实现图案。

+1

所以你是说NVI基本上是模板方法模式的语言特定实现,除此之外,没有真正的区别吗?你将如何使用C++模板来实现相同的结果? – 2010-07-19 20:59:03

+0

@Robert S. Barnes据我所见,使用C++模板作为模板方法没有明显的方法。模板方法说'做这个,然后做这件事',虽然你可以为其中一件或另一件事创建一个函子,但是与C++模板给你的类型参数没有真正的关系。 – 2010-07-20 19:57:53

+0

我想我仍然不明白你在这里的意思:“也可以使用模板元编程在C++中创建模板方法来消除动态分派。” – 2010-07-21 08:51:19

8

正如人们所说的,NVI是一个预设电台的成语,相关的语言种类。它已经在其他推广由Herb萨特,因为它有助于执行合同:

  • 类不变
  • 功能合同(断言在传递的参数和所产生的返回值)
  • 重复操作(如日志)
  • 过所产生的异常
  • 控制(坏主意虽然;))

然而,实现实际上可能差异很大,例如NVI的另一个例子执行是将它与平普尔结合:

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 +平普尔组合,存在露出的类没有virtual在所有(甚至没有公开)。内存布局是向后兼容的。我们已经实现二进制兼容。

在这里,我们使用几种模式一次:

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

+1为“虚拟是实现细节”和ABI考虑。 – neuro 2010-06-21 09:43:29

+0

+1。但是,我不同意你为虚拟接口制作单独的类似pimpl的类。最明显的缺点是它会使所需班级的数量增加一倍。此外,如果FooImpl不透明,它可能会诱使用户直接使用它并跳过Foo。然而,由于NVI允许人们重写虚拟实现,所以它不太可能不透明,所以FooImpl将不得不公开访问。尽管它有其自身的缺点,但我认为在一个班级实施NVI并避免使用公共虚拟功能是一种更好的方法。 – stinky472 2010-06-30 07:56:27

+0

这也是一个更容易实施的策略:没有公共虚拟函数,而是为提供公共虚拟接口的类pimpl类提供特殊情况。 – stinky472 2010-06-30 07:58:07

相关问题