编译器内部如何解决C++中的钻石问题?
我们知道可以使用虚拟继承来解决钻石问题。
例如:
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();
}
当我们编译这段代码时,我们会得到一个歧义错误。 现在我的问题是编译器如何在内部检测到这个歧义问题(钻石问题)。
We know that we can solve the diamond problem using virtual inheritance.
For example:
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();
}
When we compile this code we will get an ambiguity error.
Now my question is how compiler internally detects this ambiguity problem (diamond problem).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
编译器构建的表列出了每个类的所有成员,并且还具有允许它在任何类的继承链中上下移动的链接。
当需要查找成员变量(示例中的权重)时,编译器从实际类(在您的例子中为 Liger)开始。它在那里找不到权重成员,因此它会向上移动一级到父类。在本例中,有两个,因此它会扫描 Tiger 和 Lion 来查找名称权重的成员。仍然没有任何命中,所以现在需要再升一级,但需要执行两次,该级别的每个班级一次。这将继续下去,直到在继承树的某个级别找到所需的成员。如果在任何给定级别,考虑到所有多重继承分支,它只找到一个成员,那么一切都很好,如果它找到两个或多个具有所需名称的成员,那么它无法决定选择哪一个,因此会出错。
The compiler builds tables that list all the members of every class, and also has links that allow it to go up and down the inheritance chain for any class.
When it needs to locate a member variable (weight in your example) the compiler starts from the actual class, in your case Liger. It won't find a weight member there, so it then moves one level up to the parent class(es). In this case there are two, so it scans both Tiger and Lion for a member of name weight. There aren't still any hits, so now it needs to go up one more level, but it needs to do it twice, once for each class at this level. This continues until the required member is found at some level of the inheritance tree. If at any given level it finds only one member considering all the multiple inheritance branches everything is good, if it finds two or more members with the required name then it cannot decide which one to pick so it errors.
当编译器为类创建函数指针表时,每个符号必须在其中出现一次。在此示例中,
getWeight
出现了两次:在Tiger
中和在Lion
中(因为Liger
没有实现它,所以它会沿着树向上寻找它),因此编译器会被卡住。实际上,这很简单。
When compiler creates a table of function pointers for a class, every symbol must appear in it exactly once. In this example,
getWeight
appears twice: inTiger
and inLion
(becauseLiger
doesn't implement it, so it goes up the tree to look for it), thus the compiler gets stuck.It's pretty simple, actually.
在您的代码中,liger 的结构是
如果您从
Liger
指针调用Animal
函数,实际上 Liger 可以转换为两个 Animal (因此存在歧义)继承将生成一个类似于 There is now just one Animal 的结构
,可以从两个基础间接访问,因此从 Liger 到 Animal 的转换不再含糊不清。
With your code, the structure for liger is
If you call an
Animal
function from aLiger
pointer, there are actually two Animal a Liger can convert to (hence the ambiguity)Virtual inheritance will generate a structure like
There is now just one Animal, indirectly reachable from both of the bases, so a conversion from Liger to Animal is anymore ambiguous.
编译器不会检测到歧义问题。编译器只对代码中使用的内容的声明感兴趣。如果一个元素被声明多次,编译器不会抱怨。
歧义问题来自链接器。链接器在对象 lg 的继承树中看到 getWeight() 函数的两个定义,并且不知道选择哪个定义来与调用 lg.getWeight() 链接。这就是歧义所在。
the compiler does not detect the ambiguity problem. the compiler is only interested in the declarations of what is being used in the code. the compiler does not complain if an element is declared more than once.
the ambiguity problem comes from the linker. the linker sees two definitions of the getWeight() function within the inheritance tree of the object lg, and does not know which definition to choose to link with the call lg.getWeight(). so that's the ambiguity.