类 中具有相同变量名的多重继承

Multiple inheritance with the same variable name in the classes

我不小心 运行 遇到了在多重继承中使用 类 中同名成员变量的问题。我的基本想法是成员变量很简单 "merged",即发生多重声明。编译器甚至没有告诉我警告,请参阅下面的 MWE。 我知道使用同名变量是个坏主意,所以我认为至少以我的方式引用它们是模棱两可的;所以我预计至少会有一个警告或者一个错误。

1) 为什么编译器至少不写出警告?

2) 这些变量的处理是如何在内部解决的? (我想像 HW::I 和 Other::I 这样的别名被使用了,但是它们与 SW1::I 和 SW2::I 有什么关系?)

#include <iostream>
struct Other { int I;};
struct HW { int I;};
struct SW1 : Other, HW { int I;};
struct SW2 : HW, Other { int I;};
struct D : SW1 { };
struct E : SW2 { };

int main()
{
    E* e = new E;
    D* d = new D;
    e->I = 3;
    SW1* pc1 = dynamic_cast<SW1*>(d);
    pc1->I = 2;
    std::cerr << d->I;
    std::cerr << e->I;
    SW2* pc2 = dynamic_cast<SW2*>(e);
    pc2->I = 1;
    std::cerr << d->I;
    std::cerr << e->I;
}

变量没有合并,您只需同时获得全部 3 个即可。您需要将指针转换为正确的类型才能访问您想要的变量。

#include <iostream>
struct Other { int I; };
struct HW { int I; };
struct SW1 : public Other, public HW { int I; };
struct D : public SW1 { };

int main() {
    D* d = new D;
    d->I = 1;
    SW1* pc1 = dynamic_cast<SW1*>(d);
    pc1->I = 2;
    static_cast<Other*>(pc1)->I = 3;
    static_cast<HW*>(pc1)->I = 4;
    std::cerr << d->I;
    std::cerr << static_cast<Other*>(d)->I;
    std::cerr << static_cast<HW*>(d)->I;
}

打印:

234

即同一个对象 d 包含 3 个不同版本的 I,具体取决于您如何看待它。

Why the compiler does not write out at least a warning?

因为你没有写错任何危险或模棱两可的东西。你或我可能会感到困惑,但编译器有一组特定的查找规则来处理它。

当你写一个 class 成员访问表达式如 e->I 时,编译器不只是查找名称 I,它查找 sub -object 包含以这种方式命名的成员以及该成员。它还从最派生的对象类型开始,并在基 class 子对象处查找 "up" 直到找到某些东西(简而言之,这也是隐藏在 C++ 中的成员名称的工作方式)。

因此对于 e->I,它会在 E 中查找 I。该搜索没有找到任何内容,因此它进入基础 class 主题。它找到 SW2::I,一个引用 SW2 中定义的唯一成员的名称。所以它停止了。

如果没有SW2::I,它将继续查找并找到Other::IHW::I。现在在两个不同的基础 class 子对象中发现了相同的名称,我们得到了一个歧义。不是歧义警告,而是让表达式 e->I 变得模棱两可,这是一个错误。

编译器没有诊断您的代码有任何问题是正确的。代码,正如您构建的那样,是明确的。本质上,如果一个名称与多个变量(或您的情况下的 class 成员)同样匹配良好,则该名称是不明确的。

评估 e->I 时,找到的第一个候选者是 I,它是 class SW2 的成员(通过继承)。 SW2 从其基础 class 继承的 I 的成员不如 Sw2 直接定义的成员匹配得好。

同理,pc1->I明确为SW1的成员,d->I同理,pc2->I明确为基class的成员SW2.

如果 SW2 没有自己的名为 I 的成员(即 struct SW2: HW, Other {};( ,则在评估 e->I 时会出现歧义。在这种情况下,在评估 e->I,名称解析在 SW2 中查找名为 I 的成员,但没有找到。解析名称然后考虑两个基数 classes,HWOther,它们都有一个名为 I 的成员。它们同样匹配良好,因此表达式 e->I 是不明确的 - 编译器将发出诊断信息,即错误(不仅仅是警告)。在这种情况下,程序员可以使用范围 (::) 运算符显式解决歧义。例如,e->HW::Ie->Other::I 完全限定名称.

你是对的,在 class 层次结构中多次使用一个名称是个坏主意。一方面是因为很难以对编译器有意义的方式正确解决歧义,另一方面是因为凡人通常难以理解逻辑。