有没有办法禁止我的类的子类化?
Posted
技术标签:
【中文标题】有没有办法禁止我的类的子类化?【英文标题】:Is there a way to forbid subclassing of my class? 【发布时间】:2010-11-17 20:11:55 【问题描述】:假设我有一个名为“Base”的类,以及一个名为“Derived”的类,它是 Base 的子类,可以访问 Base 的受保护方法和成员。
我现在要做的就是让它没有其他类可以继承 Derived。在 Java 中,我可以通过将 Derived 类声明为“final”来实现这一点。有什么 C++ 技巧可以给我同样的效果吗?
(理想情况下,我想让除 Derived 之外的任何类都不能继承 Base。我不能只将所有代码放入同一个类或使用friend关键字,因为 Base 和 Derived 都是模板化,Base 的模板参数比 Derived 少....)
【问题讨论】:
C++ FAQ Lite 对此有 a topic。 我会包含一些源代码 - 因为它涉及模板。 确实如此……然而,这些技术似乎都不是完全令人满意的:#1 意味着 Derived 的用户必须使用非标准的习惯用法才能使用该类;他们不能像往常一样直接声明它;相反,您必须调用命名构造函数。 #2 is not 不会在尝试子类化时产生任何编译时错误(这是我正在寻找的效果) #3 将向我的对象添加一个 vtable,我希望避免这种情况。 在这种情况下,您的问题(是否有技术)的答案几乎肯定是“否”。 @Jeremy - 没错,正如大卫建议的那样,你可能不走运。 【参考方案1】:您可以为 'Derived
' 使用私有构造函数,并为实例化使用公共静态 Create 函数
【讨论】:
【参考方案2】:禁止子类化的最简单方法是将构造函数设为私有:
class Foo
private:
Foo()
public:
static Foo* CreateFoo() return new Foo;
;
编辑:感谢 Indeera 指出这需要一个静态工厂方法
【讨论】:
啊,但是您将如何创建 Foo 的实例? :) 其实Foo::CreateFoo()
,虽然它为什么返回一个指针对我来说是个谜,因为这个类可以完美地复制......
你说的最简单的方法。有哪些替代方案?【参考方案3】:
没有简单而干净的方法。
标准库所做的只是使析构函数成为非虚拟的。这不会阻止子类化,但它向用户发出了一个强烈的信号,表明它不是为继承而设计的,这意味着在使用派生类时必须非常小心。
但最终,您是否需要绝对让子类化不可能?表明“从这个类派生是一个坏主意”不是很好吗?
如果他们真的愿意,人们总是可以破坏你的代码。你能做的最好的就是让他们知道他们应该做什么和不应该做什么,并希望他们不会主动尝试破坏你的代码。
保护您的代码免受墨菲的侵害,而不是马基雅维利。 ;)
【讨论】:
是否有工具会在您忽略此“强信号”时发出警告。【参考方案4】:由于您使用的是模板,我认为您关于防止除 Derived 以外的任何类从 Base 到子类的问题的最后一部分可以使用适当的部分专业化来完成。
以下代码 sn-p 是我想出的,但所需的复杂性只会加强 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 这样的类工作。
【讨论】:
【参考方案5】:从 C++11 开始,您可以将 final 关键字(技术上是一个特殊的标识符,因为它实际上不是关键字)添加到您的类中,例如
class Derived final
...
你可以在http://en.wikipedia.org/wiki/C++11#Explicit_overrides_and_final阅读更多关于final关键字的信息
【讨论】:
+1 使用 C++11 提供此解决方案很有趣。指向更详细描述的附加链接将使此答案更有帮助。以上是关于有没有办法禁止我的类的子类化?的主要内容,如果未能解决你的问题,请参考以下文章