我不小心遇到了在多重继承中使用的类中具有相同名称的成员变量的问题。我的基本想法是成员变量是简单的“合并”,即发生了多个声明。编译器甚至没有告诉我警告,请参阅下面的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;
}
答案 0 :(得分:4)
为什么编译器至少不会写出警告?
因为您没有写错,危险或模棱两可的东西。您或我可能会感到困惑,但是编译器具有一组特定的查找规则来处理它。
当您编写类似e->I
的类成员访问表达式时,编译器不仅会查找名称I
,还会查找包含以下内容的子对象成员以这种方式命名,以及该成员。它也从派生程度最高的对象类型开始,并在基类子对象上“向上”查找,直到找到某物为止(概括地说,这也是C ++中隐藏成员名称的方式)。
因此,对于e->I
,它将在I
中寻找E
。该搜索一无所获,因此进入基类主题。它找到SW2::I
,该名称引用SW2
中定义的唯一成员。这样就停止了。
如果没有SW2::I
,它将继续查找并找到Other::I
和HW::I
。现在,在两个不同的基类子对象中找到了相同的名称,这导致了歧义。并不是警告歧义,而是使表达式e->I
模棱两可,这是一个错误。
答案 1 :(得分:2)
编译器正确无误,无法诊断代码问题。正如您所构造的那样,该代码并不模糊。从本质上讲,如果一个名称与多个变量(或者您的情况下的类成员)具有相同的匹配性,那么该名称就很含糊。
在评估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
可以完全限定名称。
您是正确的,在类层次结构中如此多次使用名称是一个坏主意。既因为很难以对编译器有意义的方式正确地解决歧义,又因为仅仅凡人通常很难遵循逻辑。
答案 2 :(得分:1)
变量不会合并,您只需同时获得所有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
,具体取决于您如何查看。