我不小心碰到具有在多重继承使用类的同名成员变量的问题。我的基本想法是,成员变量是简单的“合并”,即多发生宣言。编译器没有告诉我甚至警告,请参见下面的MWE。据我所知,这是一个坏主意,有相同名称的变量,所以我认为这至少是不明确的提及他们在我的方式;所以我预计将有至少一个警告或可能的错误。
1)为什么编译器不会写出至少一个警告?
2)如何这些变量的处理在内部解决了吗? (我想喜欢HW ::我和其他::我使用别名,但它们与SW1 ::我和SW2 ::我?)
#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;
}
编译器是不正确的诊断你的代码的任何问题。该代码,因为你已经构建它一点也不含糊。从本质上讲,一个名称不明确,如果它是一个以上的变量(或类成员在你的情况)同样很好的匹配。
当评估e->I
,发现第一候选是类I
的成员(通过继承)的SW2
。 I
的那SW2
从它的基类继承的成员是不如匹配如由Sw2
直接定义的构件。
类似地,pc1->I
是明确SW1
的构件,d->I
是相同的,并且是pc2->I
明确的基类SW2
的构件。
如果e->I
没有自己的成员命名SW2
歧义会发生在评估I
(即struct SW2: HW, Other {};
(在这种情况下,评估e->I
时,名称解析看起来SW2
的成员名为I
,并没有找到它。解决的名则认为这两个基类,HW
和Other
,这两者有一个成员叫I
他们同样精彩的比赛,所以表达e->I
是模糊的 - 编译器会发出诊断即错误(不只是一个警告)。在这种情况下,有可能程序员明确解决使用范围(::
)操作的模糊性。例如,e->HW::I
或e->Other::I
充分资格的名称。
你是正确的,一类层次结构中的名称,例如多使用是一个坏主意。这既是因为它可能很难正确解决的方式,有意义的编译器的不确定性,因为凡人往往有逻辑如下麻烦。
为什么编译器不会写出至少一个警告?
因为你没有写任何错误,危险或不明确的。你或我可能会混淆,但是编译器有一组特定的查找规则来处理它。
当你写像e->I
类成员访问表达式,编译器不只是看这名字I
,它查找子对象,其中包含这样命名的成员,该成员一起。它也开始用最派生的对象类型,并查找“向上”在基类的子对象,直到找到的东西(这也是如何使会员名躲藏在C ++的工作,简而言之)。
因此,对于e->I
,它会在I
E
。该搜索发现什么都没有,所以它进入的基类主题。它发现SW2::I
,是指SW2
定义的唯一成员的名称。因此,它停止。
如果没有SW2::I
,它会继续寻找和发现都Other::I
和HW::I
。现在同样的名称在两个不同的基类的子对象中,我们得到一个模棱两可。不是模糊的警告,但平坦出来使表达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
,这取决于你如何看待它。