假设我有一个名为“Base”的类,以及一个名为“Derived”的类,它是Base的子类,并访问受保护的方法和Base的成员。
我现在要做的是让其他类不能继承Derived。在Java中,我可以通过声明Derived类“final”来实现这一点。是否有一些C ++技巧可以给我同样的效果?
(理想情况下,我希望除了Derived之外没有其他类可以将Base子类化。我不能只将所有代码放入同一个类或使用friend关键字,因为Base和Derived都是模板化,Base的模板参数少于Derived ....)
答案 0 :(得分:13)
从C ++ 11开始,你可以在你的班级中添加final关键字(技术上是一个特殊的标识符,因为它实际上不是关键字),例如
class Derived final
{
...
您可以在http://en.wikipedia.org/wiki/C++11#Explicit_overrides_and_final
了解有关最终关键字的更多信息答案 1 :(得分:9)
您可以拥有“Derived
”的私有构造函数和用于实例化的公共静态Create函数
答案 2 :(得分:5)
禁止子类化的最简单方法是将构造函数设为私有:
class Foo
{
private:
Foo() {}
public:
static Foo* CreateFoo() { return new Foo; }
};
编辑:感谢Indeera指出这需要一个静态的Factory方法
答案 3 :(得分:3)
没有简单而干净的方法。
标准库的作用只是使析构函数非虚拟化。这并不妨碍子类化,但它向用户发出强烈信号,表明它不是为继承而设计的,这意味着在使用派生类时必须非常小心。
最终,你需要绝对让子类不可能吗?是否足以表明“从这个阶级中衍生出来是一个坏主意”?
如果他们真的想要,人们总是会破坏你的代码。你能做的最好的事情就是让他们知道他们应该做什么和不该做什么,并希望他们不会主动尝试来破坏你的代码。
保护您的代码免受Murphy的攻击,而不是Machiavelli。 ;)
答案 4 :(得分: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的部分特化中使构造函数受到保护,但这样就可以使DishonestDerived这样的类工作。