根据“C ++编码标准”第32项中的描述,我有一个值类。简而言之,这意味着它提供了值语义,并且没有任何虚拟方法。
我不希望一个类派生自这个类。除了其他人之外,一个原因是它有一个公共的非虚拟析构函数。但是,基类应该具有公共和虚拟或受保护且非虚拟的析构函数。
我不知道编写值类的可能性,因此不可能从中派生它。我想在编译时禁止它。是否有任何已知的成语可以做到这一点?如果没有,或许在即将到来的C ++ 0x中有一些新的可能性?还是有充分的理由说没有这种可能性?
答案 0 :(得分: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开始)。
答案 1 :(得分:8)
如果您愿意只允许通过工厂方法创建类,则可以使用私有构造函数。
class underivable {
underivable() { }
underivable(const underivable&); // not implemented
underivable& operator=(const underivable&); // not implemented
public:
static underivable create() { return underivable(); }
};
答案 2 :(得分:6)
即使问题未标记为C ++ 11,对于到达此处的人员,应该提到C ++ 11支持新的上下文标识符final
。见wiki page
答案 3 :(得分:4)
好好看看here 这真的很酷,但这是一个黑客 不知道为什么stdlib不会用它自己的容器来做这件事。
答案 4 :(得分:2)
嗯,我遇到了类似的问题。这是在SO上发布的here。问题是另一种方式;即只允许您允许的那些类派生。检查它是否能解决您的问题。
这是在编译时完成的。
答案 5 :(得分:1)
我通常会这样做:
// This class is *not* suitable for use as a base class
评论位于标题和/或文档中。如果您的类的客户端不遵循数据包的说明,那么在C ++中,他们可能会遇到未定义的行为。未经许可获得只是一个特例。他们应该使用构图。
顺便说一句,这有点误导:“基类应该有一个公共和虚拟或受保护和非虚拟的析构函数。”
对于将用作运行时多态性基础的类来说,这是正确的。但是,如果派生类永远不会通过指向基类类型的指针引用,则没有必要。拥有仅用于静态多态的值类型可能是合理的,例如使用模拟动态绑定。令人困惑的是,继承可以在C ++中用于不同的目的,需要来自基类的不同支持。这意味着虽然您不希望在类中使用动态多态,但是如果它们被正确使用,创建派生类可能会很好。
答案 6 :(得分:0)
此解决方案不起作用,但我将其作为不做的示例。
我暂时没有使用过C ++,但据我记忆,你可以通过将析构函数设为私有来获得你想要的东西。
更新:
在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类无法使用。留下答案