在C++中防止类继承

rin*_*ng0 56 c++ inheritance controls

最近我的一位朋友问我如何在C++中阻止类继承.他希望编译失败.

我在考虑它并找到了3个答案.不确定哪个是最好的.

1)私人建设者

class CBase
{

public:

 static CBase* CreateInstance() 
 { 
  CBase* b1 = new CBase();
  return b1;
 }

private:

 CBase() { }
 CBase(CBase3) { }
 CBase& operator=(CBase&) { }


};
Run Code Online (Sandbox Code Playgroud)

2)使用CSealed基类,私有ctor和虚拟继承

class CSealed
{

private:

 CSealed() {
 }

 friend class CBase;
};


class CBase : virtual CSealed
{

public:

 CBase() {
 }

};
Run Code Online (Sandbox Code Playgroud)

3)使用CSealed基类,受保护的ctor和虚拟继承

class CSealed
{

protected:

 CSealed() {
 }

};

class CBase : virtual CSealed
{

public:

 CBase() {
 }

};
Run Code Online (Sandbox Code Playgroud)

以上所有方法都确保不能继承CBase类.我的问题是:

1)哪种方法最好?还有其他方法吗?

2)除非CSealed类是虚拟继承的,否则方法2和3将不起作用.这是为什么 ?它与vdisp ptr有什么关系吗?

PS:

上述程序是在MS C++编译器(Visual Studio)中编译的.参考:http://www.codeguru.com/forum/archive/index.php/t-321146.html

Pet*_*wis 72

从C++ 11开始,您可以将final关键字添加到您的类中,例如

class CBase final
{
...
Run Code Online (Sandbox Code Playgroud)

我可以看到想要这样做的主要原因(以及我来寻找这个问题的原因)是将一个类标记为非子类,这样你就可以安全地使用非虚析构函数并完全避免使用vtable.

  • 还有另一个很好的理由,即阻止派生类破坏不可变类的契约. (2认同)

小智 11

您无法阻止继承(在C++ 11的final关键字之前) - 您只能阻止继承类的实例化.换句话说,没有办法阻止:

class A { ... };

class B : public A { ... };
Run Code Online (Sandbox Code Playgroud)

您可以做的最好的事情是防止B类对象被实例化.在这种情况下,我建议你接受kts的建议,并记录A(或其他)不打算用于继承,给它一个非虚拟析构函数,没有其他虚函数,并留下它的事实.

  • 请注意,在C++ 11中,您可以轻松地阻止继承. (6认同)

Kit*_*YMG 10

您正在进行扭曲以防止进一步的子类化.为什么?记录该类不可扩展并使dtor非虚拟的事实.根据c的精神,如果有人真的想忽略你打算使用它的方式,为什么要阻止它们呢?(我从未final在java中看到类/方法的重点).

//Note: this class is not designed to be extended. (Hence the non-virtual dtor)
struct DontExtened
{
  DontExtened();
  /*NOT VIRTUAL*/
  ~DontExtened();
  ...
};
Run Code Online (Sandbox Code Playgroud)

  • -1:这种扭曲是针对事故的.此外,这不符合OP的问题(即使它*可能*(它不是)是XY问题).您的答案相当于将所有内容公开并记录每个逻辑上的私有项目.我的规则是让编译器尽可能帮助你不犯错误. (13认同)
  • 我认为Java的一点是,如果类是最终的,JIT编译器可以优化对虚方法的调用 (12认同)
  • 为了这个目的,我认为C++ 11有'final`.因此,我希望这是在C++ 11中这样做的惯用方法.为什么在有语言功能时使用注释来完全执行注释尝试(但是失败)执行的操作? (5认同)
  • 我同意@ThomasEding的`final`标识符,是更好的选择.它允许编译器为您提供帮助,并且非常精确地记录此类不用于继承.如果您选择采用注释路径,那么您将让程序员查找源代码并解释注释.哪个可以是多行,甚至可能在更大的注释块中.通过使用`final`,IDE可以在最初尝试从类继承时提供帮助.除了使用`final`之外,开发人员知道在哪里看,即使他/她不习惯使用的编程风格. (4认同)

Who*_*ver 5

1)是品味问题.如果我看到它正确,你的更好看的第二和第三解决方案在某些情况下将错误从链接时间移动到编译时间,这通常应该更好.

2)需要虚拟继承来强制责任将(虚拟)基类初始化为基类ctor不再可达的最派生类.