有没有办法禁止我的类的子类化?

发布于 2024-07-30 18:43:34 字数 299 浏览 11 评论 0原文

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

我现在想做的是使其他类不能子类化 Derived。 在 Java 中,我可以通过将派生类声明为“final”来实现这一点。 有一些 C++ 技巧可以给我同样的效果吗?

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

Say I've got a class called "Base", and a class called "Derived" which is a subclass of Base and accesses protected methods and members of Base.

What I want to do now is make it so that no other classes can subclass Derived. In Java I can accomplish that by declaring the Derived class "final". Is there some C++ trick that can give me the same effect?

(Ideally I'd like to make it so that no class other than Derived can subclass Base as well. I can't just put all the code into the same class or use the friend keyword, since Base and Derived are both templated, with Base having fewer template arguments than Derived does....)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

分分钟 2024-08-06 18:43:34

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

class Derived final
{
...

您可以在 http://en.wikipedia.org/wiki/C++11#Explicit_overrides_and_final

As of C++11, you can add the final keyword (technically a special identifier since it is not actually a keyword) to your class, eg

class Derived final
{
...

You can read more about the final keyword at http://en.wikipedia.org/wiki/C++11#Explicit_overrides_and_final

来日方长 2024-08-06 18:43:34

您可以使用“Derived”的私有构造函数和用于实例化的公共静态 Create 函数

You can have a private constructor for 'Derived' and a public static Create function for instantiation

苍景流年 2024-08-06 18:43:34

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

class Foo
{
private:
    Foo() {}

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

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

The easiest way to prohibiting subclassing is by making the constructor private:

class Foo
{
private:
    Foo() {}

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

Edit: Thanks to Indeera for pointing out that this needs a static Factory method

肩上的翅膀 2024-08-06 18:43:34

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

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

但最终,您是否需要绝对使子类化不可能? 难道这还不足以表明“从这个类派生是一个坏主意”吗?

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

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

There is no simple and clean way to do it.

What the standard library does is simply make the destructor nonvirtual. That doesn't prevent subclassing, but it is a strong signal to users that it is not designed for inheritance, and it means you have to be very careful when using the derived class.

Ultimately though, do you need to absolutely make subclassing impossible? Isn't it good enough to indicate that "deriving from this class is a bad idea"?

People can always break your code if they really want to. The best you can do is make them aware of what they should and shouldn't do, and hope they won't actively try to break your code.

Protect your code against Murphy, not Machiavelli. ;)

二手情话 2024-08-06 18:43:34

由于您使用的是模板,我认为您关于防止除从 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 这样的类工作。

Since you are using templates I was thinking that the last part of your question about preventing any class other than Derived to subclass from Base could be done using appropriate partial specialisations.

The following code snippet is what I came up with but the complexity required only goes to reinforce the answer by jalf. Is it worth it? If anything this has helped me understand partial specialisation more than working out a technique I would ever use in practice.

I use COMMON to indicate a shared template parameter between Base and Derived and EXTRA to denote the extra parameters that you say Derived has. The actual numbers of these could be anything I just happened to have picked one and two for these respectively.

// 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;

This code was tested with gcc 4.3.2.

Note that an alternative to the friend declaration would be to make the constructor protected in the partial specialisation of Base but then that would allow classes like DishonestDerived to work.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文