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

Multiple inheritance with the same variable name in the classes

本文关键字:变量名 多重继承      更新时间:2023-10-16

我意外地遇到了在多重继承中使用的类中具有相同名称的成员变量的问题。我的基本想法是,成员变量是简单的"合并",即发生多重声明。编译器甚至没有告诉我一个警告,请参阅下面的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;
}

为什么编译器至少不写一个警告?

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

当您编写像e->I这样的类成员访问表达式时,编译器不仅会查找名称I,还会查找包含以这种方式命名的成员和成员的子对象。它还从最派生的对象类型开始,在基类子对象上"查找",直到找到一些东西(简而言之,这也是隐藏在C++中的成员名称的工作方式)。

所以对于e->I,它在E中查找I。该搜索一无所获,因此进入基类主题。它找到SW2::I,一个引用SW2中定义的唯一成员的名称。所以它停止了。

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

编译器不诊断代码中的任何问题是正确的。正如您构建的代码一样,它并不含糊。从本质上讲,如果一个名称与多个变量(或者在您的情况下是类成员)同样匹配,那么它就是不明确的。

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

类似地,pc1->I明确地是SW1的成员,d->I是相同的,并且pc2->I明确地是基类SW2的成员。

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