C++ 从没有虚拟方法的基类继承是一种糟糕的做法吗?

C++ 从没有虚拟方法的基类继承是一种糟糕的做法吗?,c++,inheritance,C++,Inheritance,一段时间前,我读了一个关于动态演员的问题的答案。由于基类没有虚拟方法,动态\u强制转换无法工作。其中一个答案说,从没有虚拟方法的类派生通常意味着糟糕的设计。这是正确的吗?即使没有利用多态性,我仍然看不出这样做的错误。这取决于我们在谈论什么: 对于Traits类(无数据),这很好(std::一元函数) 对于private继承(用于代替组合以从空基优化中获益),它也很好 当您开始以多态方式处理此类派生对象时,问题就出现了。如果你曾经获得过这样的职位,那一定是代码气味 注意:即使在上面提到的情况下

一段时间前,我读了一个关于动态演员的问题的答案。由于基类没有虚拟方法,动态\u强制转换无法工作。其中一个答案说,从没有虚拟方法的类派生通常意味着糟糕的设计。这是正确的吗?即使没有利用多态性,我仍然看不出这样做的错误。

这取决于我们在谈论什么:

  • 对于Traits类(无数据),这很好(
    std::一元函数
  • 对于
    private
    继承(用于代替组合以从空基优化中获益),它也很好
当您开始以多态方式处理此类派生对象时,问题就出现了。如果你曾经获得过这样的职位,那一定是代码气味


注意:即使在上面提到的情况下,你仍然可以使用多态性来使用这个类,因此你会暴露在微妙的bug中。

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

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


有时,我们不是在寻找多态行为。没关系,我们有这个选择是有原因的。如果是这样的话,那么考虑使用私有继承来代替——如果你的类不是多态的,那么就没有理由尝试多态地使用它。

这里是一个好的例子,将行为因素化为策略(注意受保护的析构函数):

另一个Ok示例,用于避免模板中的代码膨胀。请注意受保护的析构函数:

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
{ ... };
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> { ... };

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

< P> C++标准库中的某些类具有受保护成员(它们对派生类仅有意义),但没有虚拟成员函数。也就是说,它们是为派生而设计的,没有虚拟。这证明了从一个没有虚拟对象的类派生出来的设计通常是糟糕的


干杯,我不会仅仅为了代码重用而使用继承。组合使我们更适合该任务,使用继承使您与基类的联系更紧密,并使您暴露于微妙的(转换/隐藏)错误中。@Matthieu:一个有效的参数,尽管仍然有争议。的确,它确实需要用户的关注和责任。我同意组合通常更好,但私有继承仍然是一种选择(尽管正确使用更难)。我发现私有继承在两种情况下是可行的:作为优化(EBO)或重写虚拟方法(这里不关心)。其他的都是懒惰/方便的问题。我有没有提到我对继承/组合的争论很紧张?私有继承只是组合(has-a而不是is-a关系)。它还有EBO的额外好处,还可以使用
使用
指令直接公开某些基本方法,作为类的
公共
受保护的
接口的一部分。我发现
公共
继承与此有关。@Alexandre C:策略和代码膨胀示例都可以使用组合来代替(只要不需要EBO)。CRTP示例实际上非常特殊,因为它不允许传统意义上的多态性(基类取决于派生类)。@Matthieu:代码膨胀和策略示例通常具有一些非平凡的公共接口。如果你让他们成为会员,那么你必须手工编写包装。通用代码和C++03很麻烦(使用C++0x和完美的转发更容易)。@Matthieu:这不是懒惰。我会尽我所能使用这些东西。多态性使用的好处是——尽管如此,如果你的类按照你所概述的正确定义,那么用户有责任不做额外的努力来避免这种情况。@Ken:问题在于这种责任。即使是非故意的,用户也可能弄糟并以多态方式使用该类。诚然,在这两种情况下都不太可能。在前者中,因为traits没有非静态的方法,所以它是无用的,而在后者中
private
将错误的可能性限制在类和朋友的方法上。尽管如此,它还是有可能发生。问题(大体上)在于缺少一个特性:我们希望在这里授权,而不是继承。
template <typename T>
struct some_state_which_can_be_empty { ... };

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