【问题标题】:Multiple inheritance with no function overriding. Why the ambiguity没有函数覆盖的多重继承。为什么模棱两可
【发布时间】:2015-02-22 13:27:16
【问题描述】:

这是我的课程。这只是

#include<iostream>
using namespace std;

class Base
{
public:
    int var;

    Base(int val) : var(val)
    {}

    void foo()
    {
        cout << "Class Base foo()" << endl;
    }
};

class Derived_A : public Base
{
public:
    Derived_A(int val) : Base(val)
    {}
};

class Derived_B : public Base
{
public:
    Derived_B(int val) : Base(val)
    {}
};

class Derived_AB : public Derived_A, public Derived_B
{
public:
    Derived_AB(int var1, int var2) : Derived_A(var1), Derived_B(var2)
    {}
};

int main(int argc, char** argv)
{
    Derived_AB ABDerObj(1,2);
    ABDerObj.foo();
    return 0;
}

这给了我一个编译器错误,说 foo 调用不明确。

函数 foo 不会在任何地方被覆盖。我知道 Base 类有两个实例,因为没有虚拟继承。所以有两个'var'变量。但我不明白为什么编译器在 foo 调用中有歧义。函数不是特定于实例的。它们是特定于类的。为什么编译器会报错?

我正在使用 Visual Studio 2013。

谢谢。

【问题讨论】:

  • 这就是virtual 在当前标准中的继承。
  • “函数不是特定于实例的”——你错了,它们是。

标签: c++ multiple-inheritance


【解决方案1】:

多重继承使你继承了两次函数 foo():一次作为Derived_A 的一部分,另一次作为Derived_B 的一部分,因为每个都继承了自己的BaseClass

BaseClass      BaseClass
    |               |
Derived_A      Derived_A  
     \           /
      \         /
       Derived_AB

所以编译器不知道它是否应该使用Derived_ADerived_B 子对象的数据来执行它。

解决方案:

1) 您可以在每次通话中明确消除歧义:

ABDerObj.Derived_A::foo();  // execute foo() for the Deived_A object

2) 你可以在类级别明确地消除歧义,通过定义一个通用的 foo,例如:

void foo()
{
    Derived_A::foo();   // you want all the foos called
    Derived_B::foo();
}

3) 如果您只想为您的 Derived_AB 提供一个 BaseClass,那么您应该将 BaseClass 设为 Derived_ADerived_B 的虚拟基类。那么Derived_AB 将只有一个BaseClass 并且对foo() 的调用不再模棱两可。

        BaseClass
       /        \
      /          \
Derived_A      Derived_A  
     \           /
      \         /
       Derived_AB  

(PS 在这种情况下,您还应该为 Dervived_AB. 定义一个 BaseClass() 初始化器)

【讨论】:

  • 谢谢。我的理解是,类的每个成员函数只存在一个函数副本。并且编译器隐式地将 '*this' 传递给每个成员函数,这就是成员函数可以访问实例特定成员的原因。为什么可以简单地对 (POD) 对象进行 memcopy 以创建新对象,这不也是一种原因吗?
  • @madu 事实上,非静态成员函数有一个隐含的this 参数。 不存在函数的“副本”。 函数不是对象。 成员函数不是数据成员,并且不存在于对象实例中。 memcpy 处理 POD 对象的原始内存,即类的数据成员。您可以打印出某些对象表示的各个字节,但不能打印函数。指向函数的指针不是指向对象的指针。您不能将指向函数的指针转换为 char* 并按原样使用结果。
  • @madu 从技术上讲,您的功能只有一个代码存在。但是当编译器生成对成员函数的调用时,他会向它提供指向 BaseClass 对象的“this”指针。在您的示例中,除非您将其设为虚拟继承,否则有两个不同的子对象。
  • @Christophe 好的。我现在明白了。 foo 只有一个实现,但是有两个不同的 '*this' 要传递,因为有两个 Base 类的实例。谢谢你。这就是我所困惑的。
【解决方案2】:

函数不是特定于实例的。它们是特定于类的。

这仅适用于静态函数。假设我将您的 foo() 实现更改为以下内容:

void foo()
{
    cout << "My value is " << var << endl;
}

您希望ABDerObj.foo(); 打印什么?


既然这是一个答案,那我也提供两个可能的解决方案。

如果foo 确实是特定于实例的(例如,因为它使用类字段var,您可以轻松告诉编译器您要调用哪个Base::foo()Derived_ADerived_B版本)通过指定你想使用哪一个

ABDerObj.Derived_A.foo();      // prints: My value is 1
ABDerObj.Derived_B.foo();      // prints: My value is 2

或者只是扔掉另一部分:

((Derived_A) ABDerObj).foo();  // prints: My value is 1
((Derived_B) ABDerObj).foo();  // prints: My value is 2

如果您有一段代码要使用Derived_A 功能,则后者最有用,例如:

Derived_A& ABDerObjPartA = ABDerObj;
ABDerObjPartA.foo();
// more code using ABDerObjPartA.

另一方面,如果 -- 坚持你的术语 -- foo 的定义确实取决于类而不是实例,则将其设为 static

// In Base:
static void foo()
{
    cout << "Class Base foo()" << endl;
}

// in main():
ABDerObj.foo(); // This is now OK

你应该真正研究的是你是否真的需要多重继承,尤其是同一个基类出现多次。通常,一旦您开始遇到这些消歧问题,它们就会变得更糟,对您的代码进行轻微的重新设计就会使其更易于维护。

【讨论】:

  • 谢谢。我的理解是每个非静态成员类都被编译器传递一个'*this'作为参数,这就是为什么你可以做“cout
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-29
  • 2019-08-05
  • 1970-01-01
  • 1970-01-01
  • 2018-03-14
  • 1970-01-01
相关资源
最近更新 更多