2011-08-09 68 views
5

我回到一个关于dynamic_cast的问题上看了一个答案。 dynamic_cast失败,因为基类没有虚拟方法。其中一个答案表示,从没有虚拟方法的类派生通常意味着一个糟糕的设计。它是否正确?即使没有利用多态性,我仍然看不到这样做的错误。从没有虚拟方法的基类继承不良习惯?

回答

6

这取决于我们在谈论:

  • 的性状类(无数据),它的罚款(std::unary_function想到)
  • private继承(用于替代组合物从空基地中受益优化)也没关系

当你开始在这个基类中多态地处理这样一个Derived对象时,问题出现了。如果你曾经获得这样的位置,那么它的确是代码异味

注意:即使在上面提到的罚款时,您仍然提供使用类多态的能力,并且您因此暴露自己以微妙的错误。

+0

关于多态的使用是可能好一点 - 不过,如果你的类定义正确为您介绍了,用户有责任不去加倍努力,以规避这一点。 –

+1

@Ken:问题在于这种责任。即使意想不到,用户可能会弄​​乱并且多态地使用这个类。当然,这两种情况都不太可能。在前者中,因为特征不具有非静态方法,所以它是无用的,而在后者中,“私人”将错误可能性限制在班级和朋友的方法中。尽管如此,它可能会发生。问题(大部分)是一个缺失的特性:我们希望*代表团*在这里,而不是*继承*。 –

0

C++中没有虚拟方法的继承只不过是代码重用。 我想不到没有多态性的遗传。

2

为了重用代码,从类派生始终是一个有效的选项。

有时,我们并没有寻找多态行为。没关系 - 我们有这个选择是有原因的。但是,如果是这种情况,那就考虑使用私有继承 - 如果你的类不是多态的,那么任何人都没有理由尝试多态使用它。

+2

我会*不*使用继承*只是*用于代码重用。使我们更好*更好*适合任务,并且使用继承将你更强烈地绑定到基类上,并且让你知道微妙的(转换/隐藏)错误。 –

+0

@Matthieu:一个有效的论点,但仍然有争议。诚然,它肯定需要关注,以及对用户的责任。我会同意组成通常更好,但私人继承仍然是一种替代方案(尽管如此,使用起来更加棘手)。 –

+2

我发现私有继承在两种情况下可行的:作为优化(EBO)或重写虚拟方法(这里不关心)。所有其他人都是懒惰/方便的事情。我有没有提到我对继承/组合辩论相当紧张? –

0

这里是一个不错的例子,来因素的行为纳入政策(注意保护析构函数):

struct some_policy 
{ 
    // Some non-virtual interface here 
protected: 
    ~some_policy() { ... } 

private: 
    // Some state here 
}; 

struct some_class : some_policy, some_other_policy { ... }; 

另一个好例子,以避免在模板代码膨胀。请注意受保护的析构函数:

struct base_vector 
{ 
    // Put everything which doesn't depend 
    // on a template parameter here 

protected: 
    ~base_vector() { ... } 
}; 

template <typename T> 
struct vector : base_vector 
{ ... }; 

另一个示例,称为CRTP。请注意受保护的析构函数:

template <typename Base> 
struct some_concept 
{ 
    void do_something { static_cast<Base*>(this)->do_some_other_thing(); } 

protected: 
    ~some_concept() { ... } 
}; 

struct some_class : some_concept<some_class> { ... }; 

另一个示例,称为空基优化。本身并不是真正的继承,因为允许编译器为基类(其充当私有成员)在some_class中保留空间是更为诡计的技巧。

template <typename T> 
struct some_state_which_can_be_empty { ... }; 

template <typename T> 
struct some_class : private some_state_which_can_be_empty<T> { ... }; 

作为一个经验法则,您继承的类应该具有虚拟或受保护的析构函数。

+0

我找到关于“公共”继承的。 –

+0

@Alexandre C:无论是政策和代码膨胀示例(只要不是必需EBO)可以使用组合物来代替。 CRTP示例是非常特殊的,因为它不允许传统意义上的多态(基类取决于派生类)。 –

+0

@Matthieu:代码膨胀和策略示例通常有一些不平凡的公共接口。如果你让他们成为会员,那么你必须手工编写包装。泛型代码和C++ 03中的繁琐(使用C++ 0x和完美的转发更容易)。 –

0

C++标准库中的某些类具有受保护的成员(仅对派生类有意义),但没有虚拟成员函数。即,它们被设计用于派生,而不具有虚拟。这证明,从一个没有虚拟的类派生出来的设计通常是不好的设计。

干杯& hth。,