有没有一种方法可以禁止我的类被子类化?

19

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

现在我想做的是使其他类不能继承Derived。在Java中,我可以通过声明Derived类为"final"来实现。是否有一些C++技巧可以产生相同的效果?

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

```cpp class Base final {...}; class Derived final : public Base {...}; ```
以上代码中,关键字final用于禁止其他类继承此类,所以Base和Derived都不能被继承。同时,使用public继承将Derived声明为Base的子类。

C++ FAQ Lite有关于这个问题的主题 - Sean Bright
@Jeremy - 对,正如David所建议的那样,你可能没有什么好运。 - Sean Bright
不确定为什么这被评为高票(?) 这个回答不是等同于 RTFM 吗? - Alan
@Alan:更像是“阅读手册的这一页”。第二个链接(一个主题)直接跳转到答案。“RTFM”意味着文档中存在答案,但没有具体指明在哪里。 - Conspicuous Compiler
“链接不是答案。如果必须引用相关的文本段落,那么请引用并写出你自己的答案。” - Lightness Races in Orbit
显示剩余5条评论
5个回答

15

很有趣的是,使用C++11实现这个解决方案。提供一个更详细描述的链接,会让这个答案更加有帮助。 - Wolf

9
你可以为“Derived”设置一个私有构造函数,并使用公共静态Create函数进行实例化。

6
最简单的禁止子类化的方法是将构造函数设为私有:
class Foo
{
private:
    Foo() {}

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

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

啊,但是你如何创建 Foo 的实例呢? :) - Indy9000
1
实际上Foo::CreateFoo(),虽然为什么它返回一个指针对我来说是个谜,因为这个类完全可以被复制... - Matthieu M.
你说的是最简单的方法。还有其他选择吗? - Wolf

3

没有一种简单明了的方法来实现这个。

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

然而,您是否需要绝对使子类化不可能?仅指示“从此类派生是一个坏主意”是否足够好呢?

如果人们真的想要,他们总是可以打破你的代码。你能做的最好的事情是让他们意识到他们应该和不应该做什么,并希望他们不会积极地试图破坏你的代码。

保护您的代码免受墨菲定律的影响,而不是马基雅维利的影响。 ;)


有没有工具可以警告你忽略了这个“强信号”? - Wolf

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

请注意,与friend声明的替代方案是将Base的部分特化中的构造函数设置为protected,但这样会允许像DishonestDerived这样的类正常工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接