【问题标题】:Polymorphism vs regular inheritance?多态性与常规继承?
【发布时间】:2014-09-25 17:18:39
【问题描述】:

我经常使用多态性,但我突然意识到。我的代码是这样的:

class A{

class B : public A{

class C : public A{

我使用A 类作为多态参数类型,用于传入BC 子类型:

//Accepts B and C objects
void aMethod(A* a){

但是,我给了A 一个虚拟析构函数——这是 A(以及 B 和 C)包含 vtable 指针的唯一原因。

所以我的问题是,如果我没有用虚拟析构函数声明 A,那么 A 就不会是多态的——我将无法在 aMethod()?? 中传递 B 或 C 类型的对象?

那么非多态继承只是共享代码,而多态(基类必须有一个 vtable)允许将子类型作为基类类型的参数传递?

【问题讨论】:

  • void aMethod 接受A 类型的对象?您需要它来获取多态性的指针或引用。否则,您只是在切片 BC 对象。
  • 如果您的参数像您的示例一样按值传递,您的对象将被切片。
  • 这在 10 年前可能是一个很好的问题,A) 还有其他更好的方法来实现多态行为 B) 你的标题没有反映问题 C) 继承是关于 是一个关系/属性——不管你想怎么称呼它——在它自己的核心,virtual关键字并没有改变它,它只是增加了多态性。

标签: c++ inheritance polymorphism virtual-functions


【解决方案1】:

在您提供的这段代码中,

void aMethod(A a){

a 形式参数不是多态的。它是按值传递的。当您传入BC 对象作为实际参数时,您只是将其切片A,也就是说,您正在复制A 基类仅限子对象。


关于

那么非多态继承只是共享代码,而多态(基类必须有一个vtable)允许将子类型作为基类类型的参数传递?

它结合了两个必须分开处理的问题。

从非多态类继承是关于共享代码,是的,但它也引入了 is-a 关系,这对于例如通过引用传递。

从多态类(具有一个或多个虚拟成员函数的类)继承允许您覆盖基类功能,或在基类将其作为派生类职责的地方实现它——通常通过具有纯虚拟成员函数。这意味着基类中的成员函数可以调用派生类的成员函数。此外,通过基类指针或引用调用成员函数,可以调用派生类的实现。

【讨论】:

  • 好的,但我所描述的不需要任何覆盖。我有一个正常的继承层次结构,其中基类有一个虚拟析构函数......如果我删除了虚拟 dtor - 它的正常继承。这是否意味着 aMethod() 不再起作用了?
  • @user997112:关于aMethod,您可以将析构函数设为非虚拟,或者如果它不执行任何操作,则将其完全删除。但这并不能为您节省太多,真的。它具有过早优化的味道。优化的一般建议是衡量,以发现您是否真的需要它。
【解决方案2】:

非多态类型还包含每个基类的子对象,并且向上转换仍然是隐式的。

因此,即使没有多态性,您也可以很好地传递派生类的对象。接收者将作用于基础子对象,就像多态类型的数据成员和非虚函数一样。

【讨论】:

    【解决方案3】:

    一个类的析构函数是唯一的virtual 方法,就像你的class A,是一个奇怪的东西。 AFAIK,这样的类在精心设计的代码中没有必要。

    根据您的问题,尚不清楚是否需要 virtual 析构函数(即,如果您曾经通过引用或指向类 A 的指针调用类 BC 的析构函数),但如果是的,您的代码有异味。如果不是,则只需将析构函数设为非虚拟。

    【讨论】:

    • 继承需要一个虚拟析构函数,否则对象会在析构中被切片(例如,如果你调用C * c = new C(); A * a = c; delete a,将调用析构函数,而不是C)。
    • @Leonardo 我知道(如果您完整阅读我的答案,您会注意到的)。但是像这样没有任何其他virtual 方法的类几乎没有任何用处。
    猜你喜欢
    • 2013-03-28
    • 2016-08-16
    • 2012-12-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-15
    相关资源
    最近更新 更多