2009-08-07 44 views
15

假设我有一个名为“Base”的类和一个名为“Derived”的类,它是Base的子类并访问受保护的方法和Base成员。有没有办法禁止我班的子类?

我现在要做的就是让其他类不能继承Derived。在Java中,我可以通过声明Derived类为“final”来完成此操作。是否有一些C++技巧可以给我提供相同的效果? (理想情况下,我想这样做,除Derived之外的其他类都可以继承Base。我不能只将所有代码放入同一个类或使用friend关键字,因为Base和Derived是这两个模板,具有更少的模板参数比基础衍生确实....)

+0

我会包含一些源代码 - 因为它涉及到模板。 – sylvanaar 2009-08-07 21:44:31

+0

如果一个类没有虚拟析构函数,那么你可能不应该从这个虚拟析构函数中删除它(这只是一个好的编程)。 C++认识到有时你需要扩展边界的能力,并让你从中继承。所以你问题不是一个教育的语言。 – 2009-08-08 01:00:03

+0

投票:删除关键字/最终标签,但添加** derived-class **标签代替 – fmuecke 2009-12-08 11:03:14

回答

12

由于C++ 11,你可以在最后加上关键字(技术上是一个特殊的标识符,因为它实际上不是一个关键字)到您的类,如

class Derived final 
{ 
... 

你可以阅读更多关于final关键字在http://en.wikipedia.org/wiki/C++11#Explicit_overrides_and_final

+0

+1有趣的是,使用C++ 11的解决方案。一个更详细的描述的额外链接,将使这个答案更有帮助。 – Wolf 2014-02-27 08:23:55

9

你可以有一个私人的构造为“Derived”和一个公共静态的实例创建函数

5

以禁止子类最简单的方法是通过使构造函数私有:

class Foo 
{ 
private: 
    Foo() {} 

public: 
    static Foo* CreateFoo() { return new Foo; } 
}; 

编辑:感谢Indeera的指出,这需要一个静态工厂方法

+0

啊,但你会如何创建Foo的实例? :) – Indy9000 2009-08-07 21:42:40

+0

Foo.CreateFoo() – 2009-08-08 10:55:57

+1

其实'Foo :: CreateFoo()',虽然它为什么返回一个指针对我来说是一个谜,因为类可以完美地被复制... – 2009-12-08 10:40:52

3

有没有简单和干净的方式来做到这一点。

标准库所做的只是使析构函数非虚拟化。这并不妨碍子类化,但它对用户来说是一个强烈的信号,它不是为继承而设计的,这意味着在使用派生类时必须非常小心。

最终,虽然,你是否需要绝对使子类化不可能?说明“从这个班级中学习是一个坏主意”是不是很好?

如果他们真的想要,人们总是可以破坏你的代码。最好的办法是让他们意识到他们应该做什么,不应该做什么,并希望他们不会主动尝试来破坏你的代码。

保护你的代码免受墨菲而不是马基雅维利。 ;)

+0

如果您忽略了这个“强烈信号”,是否有工具会发出警告? – Wolf 2014-02-24 14:35:52

1

由于您使用的模板,我在想,你对防止超过来源于基地继承其他任何一类问题的最后一部分可以用适当的部分特例来完成。

下面的代码片段是我想出来的,但所需的复杂性只能通过jalf来加强答案。这值得么?如果这有助于我理解部分专业化,而不是制定一种我将在实践中使用的技术。

我用普通的指示基类和派生和EXTRA表示,你说派生有额外的参数之间的共享模板参数。这些的实际数量可能是我刚刚碰巧分别挑选了一个和两个的任何数量。

// Forward declaration of class Derived 
template< class COMMON 
     , class EXTRA1 
     , class EXTRA2 > 
class Derived; 


// Definition of general class template Base 
template< class SUBCLASS 
     , class COMMON > 
class Base 
{ 
private: 
    Base() {} 
}; 


// Definition of partial specialisation of template class Base to open up 
// access to the constructor through friend declaration. 
template< class COMMON 
     , class EXTRA1 
     , class EXTRA2 > 
class Base< Derived< COMMON, EXTRA1, EXTRA2 > 
      , COMMON > 
{ 
private: 
    Base() {} 

    friend class Derived< COMMON, EXTRA1, EXTRA2 >; 
}; 


// Definition of class Derived 
template < class COMMON 
     , class EXTRA1 
     , class EXTRA2 > 
class Derived 
    : public Base< Derived< COMMON, EXTRA1, EXTRA2 > 
       , COMMON > 
{ 
public: 
    static Derived* create() { return new Derived; } 

private: 
    Derived() : Base< Derived< COMMON, EXTRA1, EXTRA2 > 
        , COMMON >() 
    { 
    } 
}; 


// Definition of class HonestDerived. 
// It supplies itself as the SUBCLASS parameter to Base. 
template < class COMMON 
     , class EXTRA1 
     , class EXTRA2 > 
class HonestDerived 
    : public Base< HonestDerived< COMMON, EXTRA1, EXTRA2 > 
       , COMMON > 
{ 
public: 
    HonestDerived() : Base< HonestDerived< COMMON, EXTRA1, EXTRA2 > 
          , COMMON >() 
    { 
    } 
}; 


// Definition of class DishonestDerived 
// It supplies Derived rather than itself as the SUBCLASS parameter to Base. 
template < class COMMON 
     , class EXTRA1 
     , class EXTRA2 > 
class DishonestDerived 
    : public Base< Derived< COMMON, EXTRA1, EXTRA2 > 
       , COMMON > 
{ 
public: 
    DishonestDerived() : Base< Derived< COMMON, EXTRA1, EXTRA2 > 
          , COMMON >() 
    { 
    } 
}; 


template< class COMMON, class EXTRA1, class EXTRA2 > 
class DerivedFromDerived 
    : public Derived< COMMON, EXTRA1, EXTRA2 > 
{ 
public: 
    DerivedFromDerived() : Derived< COMMON, EXTRA1, EXTRA2 >() 
    { 
    } 
}; 

// Test partial specialisation gives Derived access to the Base constructor 
Derived< int, float, double >* derived 
    = Derived< int, float, double >::create(); 

// Test that there is no access to the Base constructor for an honest subclass 
// i.e. this gives a compiler error 
HonestDerived< int, float, double > honestDerived; 

// Test that there is no access to the Base constructor for a dishonest subclass 
// i.e. this gives a compiler error 
DishonestDerived< int, float, double > dishonestDerived; 

// Test that there is no access to the Derived constructor 
// i.e. this gives a compiler error 
DerivedFromDerived< int, float, double > derivedFromDerived; 

这段代码是用gcc 4.3.2测试的。

注意,对朋友声明的替代方案是使基地的部分特保护的构造函数,但随后将允许像DishonestDerived类的工作。