编译器如何在内部解决C ++中的菱形问题?

时间:2011-09-12 06:00:53

标签: c++ virtual multiple-inheritance diamond-problem

我们知道我们可以使用虚拟继承解决钻石问题。

例如:

   class Animal // base class
   {
     int weight;
     public:
     int getWeight() { return weight;};
   };
   class Tiger : public Animal { /* ... */ }; 
   class Lion : public Animal { /* ... */ };
   class Liger : public Tiger, public Lion { /* ... */ }; 
   int main()
   {
     Liger lg ;
     /*COMPILE ERROR, the code below will not get past
     any C++ compiler */
     int weight = lg.getWeight();
   }

编译此代码时,我们会遇到歧义错误。 现在我的问题是编译器内部如何检测这种歧义问题(钻石问题)。

4 个答案:

答案 0 :(得分:4)

编译器构建列出每个类的所有成员的表,并且还有链接允许它在任何类的继承链中上下移动。

当需要找到一个成员变量(示例中的权重)时,编译器从实际的类开始,在你的情况下是Liger。它不会在那里找到一个权重成员,因此它会向上移动一级到父类。在这种情况下,有两个,所以它扫描虎和狮子的名称重量的成员。还没有任何命中,所以现在它需要再增加一个级别,但它需要做两次,每个级别一次。这将继续,直到在继承树的某个级别找到所需的成员。如果在任何给定的级别,它只找到一个成员考虑所有多个继承分支一切都很好,如果它找到两个或多个具有所需名称的成员,那么它无法决定选择哪一个,所以它是错误的。

答案 1 :(得分:3)

当编译器为类创建一个函数指针表时,每个符号必须只出现一次。在此示例中,getWeight出现了两次:在TigerLion中(因为Liger没有实现它,所以它会在树上查找它),因此编译器卡住了。

实际上很简单。

答案 2 :(得分:1)

编译器在Liger中查找getWeight,找不到,然后检查其父项及其父项的父项等等,如果找到多个,则会返回错误并在您身上死亡,因为它无法判断它应该使用哪一个。

答案 3 :(得分:1)

使用您的代码,liger的结构是

Liger[Tiger[Animal]Lion[Animal]]

如果从Animal指针调用Liger函数,实际上有两个动物可以转换为(因此含糊不清)

虚拟继承将生成类似

的结构
Liger[Tiger[*]Lion[Animal]]
            \-----/

现在只有一种动物,可以从两个基地间接到达,所以从Liger到Animal的转换更加模糊。