2009-06-16 79 views
11

根据“C++编码标准”第32条中的描述,我有一个值类。简而言之,这意味着它提供了值语义,并且没有任何虚拟方法。是否有可能在编译时禁止从类中派生?

我不想让一个类从这个类派生。除此之外,其中一个原因是它具有公共的非虚构析构函数。但是基类应该有一个公共和虚拟的析构函数,或者是非虚拟的。

我不知道编写值类的可能性,以至于不可能从它派生出来。我想在编译时禁止它。是否有任何已知的习惯用法呢?如果不是,那么即将到来的C++ 0x也许有一些新的可能性?还是有没有这种可能性的充分理由?

回答

6

即使问题没有标记为C++ 11,人谁得到这里应该提到的是C++ 11支持新的上下文标识符final。见wiki page

8

如果您只允许通过工厂方法创建类,则可以使用私有构造函数。

class underivable { 
    underivable() { } 
    underivable(const underivable&); // not implemented 
    underivable& operator=(const underivable&); // not implemented 
public: 
    static underivable create() { return underivable(); } 
}; 
+0

啊,这可能是我的类的静态工厂方法来保持东西放在一起?听起来很不错。 – SebastianK 2009-06-16 11:38:07

+3

不幸的是,这并不能阻止派生。它只是防止派生类型的安装,除了通过工厂。如果你真的可以预防派生,那就太好了,但据我所知,你不能。 – 2009-06-16 11:43:41

+0

实用的方式是不能实例化派生类型的对象,并且根本无法派生(访问受保护的静态成员??) – Motti 2009-06-16 18:02:41

25

了Bjarne Stroustrup的写关于这个here


从链接的相关位:

我可以阻止人们从我的类派生?

是的,但你为什么要?有两个常见的答案:

  • 效率:避免我的功能 调用是虚拟的。
  • 安全:保证我的课没有(没有切割的恐惧 例如,可以肯定 ,我可以复制的对象)

用作 基类,以我的经验,效率理由通常是错放的恐惧。在C++中,虚拟函数调用速度如此之快,以至于与使用普通函数调用的替代解决方案相比,虚拟函数调用的虚拟函数调用不会产生可测量的运行时开销。请注意,虚拟函数调用机制通常仅在通过指针或引用进行调用时使用。直接为命名对象调用函数时,虚拟函数类的开销很容易被优化。

如果确实需要“封顶”类层次结构以避免虚函数调用,那么可能会问为什么这些函数首先是虚拟的。我看到了一些例子,其中性能关键的功能因为没有正当理由而变得虚拟,仅仅是因为“我们通常这样做”。

这个问题的另一个变种,如何防止出于逻辑原因导出,有一个解决方案。不幸的是,这个解决方案并不漂亮。它依赖于层次结构中派生最多的类必须构建虚拟基础的事实。例如:

class Usable; 

class Usable_lock { 
    friend class Usable; 
private: 
    Usable_lock() {} 
    Usable_lock(const Usable_lock&) {} 
}; 

class Usable : public virtual Usable_lock { 
    // ... 
public: 
    Usable(); 
    Usable(char*); 
    // ... 
}; 

Usable a; 

class DD : public Usable { }; 

DD dd; // error: DD::DD() cannot access 
     // Usable_lock::Usable_lock(): private member 

(从D&E秒11.4.3)。

2

嗯,我有一个类似的问题。这是在SO上发布的here。这个问题是另一回事。即只允许派生你允许的类。检查它是否解决您的问题。

这在编译时间完成。

4

好好看看here
这真的很酷,但它是一个黑客。
不知道为什么stdlib不会用它自己的容器做到这一点。

0

此解决方案不起作用,但我将其作为不做的一个示例。


我没有使用C++有一段时间了,但据我记得,你得到你想通过析构函数私人的东西。

UPDATE:

在Visual Studio 2005中,你会得到警告或错误。检查了下面的代码:

class A 
{ 
public: 
    A(){} 
private: 
    ~A(){} 
}; 

class B : A 
{ 
}; 

现在,

B B;

会产生一个错误 “错误C2248: 'A ::〜A':不能访问类 'A' 声明私有成员”

B *b = new B(); 

会产生警告“警告C4624: 'B':析构函数无法生成,因为基类析构函数无法访问“。

它看起来像一个半解决方案,但正如orsogufo指出的,这样做会导致A类不可用。离开答案

1

我一般会做到这一点,如下所示:

// This class is *not* suitable for use as a base class 

评论之后,在头和/或文档。如果你的班级的客户不遵守包装上的说明,那么在C++中,他们可能会出现未定义的行为。未经许可而派生只是这种情况的特例。他们应该使用组合。

顺便说一句,这有点误导:“一个基类应该有一个公共的,虚拟的或者受保护的和非虚拟的析构函数”。

对于要用作运行时多态性基类的类也是如此。但是,如果派生类永远不会通过指向基类类型的指针来引用,那就没有必要。具有仅用于静态多态性的值类型可能是合理的,例如模拟动态绑定。令人困惑的是,继承可以用于C++中的不同目的,需要基类的不同支持。这意味着,虽然你不想动态多态性与你的类,它可能仍然是良好的创建派生类提供他们正确使用。

相关问题