18

假设我有一个名为“Base”的类和一个名为“Derived”的类,它是 Base 的子类,可以访问 Base 的受保护方法和成员。

我现在想要做的是使它没有其他类可以继承 Derived。在 Java 中,我可以通过将 Derived 类声明为“final”来实现这一点。是否有一些 C++ 技巧可以给我同样的效果?

(理想情况下,我想让除 Derived 之外的任何类都不能继承 Base。我不能将所有代码放入同一个类或使用friend关键字,因为 Base 和 Derived 都是模板化的,使用Base 的模板参数比 Derived 少....)

4

5 回答 5

15

从 C++11 开始,您可以将 final 关键字(技术上是特殊标识符,因为它实际上不是关键字)添加到您的类中,例如

class Derived final
{
...

您可以在http://en.wikipedia.org/wiki/C++11#Explicit_overrides_and_final阅读有关 final 关键字的更多信息

于 2012-09-07T13:58:42.177 回答
9

您可以为 ' Derived' 提供一个私有构造函数和一个用于实例化的公共静态 Create 函数

于 2009-08-07T21:39:40.973 回答
6

禁止子类化的最简单方法是将构造函数设为私有:

class Foo
{
private:
    Foo() {}

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

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

于 2009-08-07T21:40:22.737 回答
3

没有简单而干净的方法可以做到这一点。

标准库所做的只是使析构函数成为非虚拟的。这不会阻止子类化,但它向用户发出了一个强烈的信号,表明它不是为继承而设计的,这意味着在使用派生类时必须非常小心。

最终,您是否需要绝对使子类化成为不可能?表明“从这个类派生是一个坏主意”还不够好吗?

如果他们真的想的话,人们总是可以破坏你的代码。你能做的最好的就是让他们知道他们应该做什么和不应该做什么,并希望他们不会主动尝试破坏你的代码。

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

于 2009-08-07T22:30:30.093 回答
1

由于您使用的是模板,所以我认为您关于防止除 Derived 以外的任何类从 Base 到子类的问题的最后一部分可以使用适当的部分专业化来完成。

以下代码片段是我想出的,但所需的复杂性只会加强 jalf 的答案。这值得么?如果这比制定我在实践中使用的技术更能帮助我理解部分专业化的话。

我使用 COMMON 表示 Base 和 Derived 之间的共享模板参数,并使用 EXTRA 表示您所说的 Derived 具有的额外参数。这些的实际数量可能是我碰巧为它们分别选择了一个和两个。

// 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 进行了测试。

请注意,友元声明的替代方法是使构造函数在 Base 的部分特化中受到保护,但这样会允许像 DishonestDerived 这样的类工作。

于 2009-08-08T10:45:52.783 回答