在使用 C++ 的虚拟继承期间调用构造函数
Invoking constructors during virtual inheritance with C++
这是我在阅读this section on learncpp.com时遇到的问题。我使用了这里列出的代码,然后为了测试做了一些小改动。
背景
虚拟继承创建了对基 class 的公共引用,这有两个作用。
首先,它消除了歧义,因为只创建一次基成员的副本(例如,将 print() 函数添加到 PoweredDevice 并在 main() 中调用它,否则会导致编译器错误)。
其次,最派生的 class 负责调用基本构造函数。如果其中一个中间 classes 试图调用初始化列表中的基本构造函数,调用应该是 ignored.
问题
当我编译 运行 代码时,它 returns:
PoweredDevice: 3
PoweredDevice: 3
Scanner: 1
PoweredDevice: 3
Printer: 2
应该return:
PoweredDevice: 3
Scanner: 1
Printer: 2
当我使用 GDB (7.11.1) 跟踪执行时,它表明中间函数也在通过初始化列表调用 PoweredDevice -- 但这些应该被忽略。 PoweredDevice 的多次初始化不会导致任何成员的歧义,但确实给我带来了麻烦,因为代码执行了多次,而它应该只发生一次。对于更复杂的问题,我不习惯使用虚拟继承。
为什么这些中间 classes 仍在初始化基础?是我的编译器 (gcc 5.4.0) 有问题还是我误解了虚拟继承的工作原理?
编辑:代码
#include <iostream>
using namespace std;
class PoweredDevice
{
public:
int m_nPower;
public:
PoweredDevice(int nPower)
:m_nPower {nPower}
{
cout << "PoweredDevice: "<<nPower<<endl;
}
void print() { cout<<"Print m_nPower: "<<m_nPower<<endl; }
};
class Scanner : public virtual PoweredDevice
{
public:
Scanner(int nScanner, int nPower)
: PoweredDevice(nPower)
{
cout<<"Scanner: "<<nScanner<<endl;
}
};
class Printer : public virtual PoweredDevice
{
public:
Printer(int nPrinter, int nPower)
: PoweredDevice(nPower)
{
cout<<"Printer: "<<nPrinter<<endl;
}
};
class Copier : public Scanner, public Printer
{
public:
Copier(int nScanner, int nPrinter, int nPower)
:Scanner {nScanner, nPower}, Printer {nPrinter, nPower}, PoweredDevice {nPower}
{ }
};
int main()
{
Copier cCopier {1,2,3};
cCopier.print();
cout<<cCopier.m_nPower<<'\n';
return 0;
}
这似乎是一个 GCC 错误,当统一初始化与虚拟继承一起使用时触发。
如果我们改变:
Copier(int nScanner, int nPrinter, int nPower)
:Scanner {nScanner, nPower}, Printer {nPrinter, nPower}, PoweredDevice {nPower}
{ }
至:
Copier(int nScanner, int nPrinter, int nPower)
:Scanner (nScanner, nPower), Printer (nPrinter, nPower), PoweredDevice (nPower)
{ }
错误消失,并且按预期运行:
PoweredDevice: 3
Scanner: 1
Printer: 2
Print m_nPower: 3
3
Clang 和 Visual Studio 都能够正确编译原始代码,并给出预期的输出。
这是我在阅读this section on learncpp.com时遇到的问题。我使用了这里列出的代码,然后为了测试做了一些小改动。
背景
虚拟继承创建了对基 class 的公共引用,这有两个作用。
首先,它消除了歧义,因为只创建一次基成员的副本(例如,将 print() 函数添加到 PoweredDevice 并在 main() 中调用它,否则会导致编译器错误)。
其次,最派生的 class 负责调用基本构造函数。如果其中一个中间 classes 试图调用初始化列表中的基本构造函数,调用应该是 ignored.
问题
当我编译 运行 代码时,它 returns:
PoweredDevice: 3
PoweredDevice: 3
Scanner: 1
PoweredDevice: 3
Printer: 2
应该return:
PoweredDevice: 3
Scanner: 1
Printer: 2
当我使用 GDB (7.11.1) 跟踪执行时,它表明中间函数也在通过初始化列表调用 PoweredDevice -- 但这些应该被忽略。 PoweredDevice 的多次初始化不会导致任何成员的歧义,但确实给我带来了麻烦,因为代码执行了多次,而它应该只发生一次。对于更复杂的问题,我不习惯使用虚拟继承。
为什么这些中间 classes 仍在初始化基础?是我的编译器 (gcc 5.4.0) 有问题还是我误解了虚拟继承的工作原理?
编辑:代码
#include <iostream>
using namespace std;
class PoweredDevice
{
public:
int m_nPower;
public:
PoweredDevice(int nPower)
:m_nPower {nPower}
{
cout << "PoweredDevice: "<<nPower<<endl;
}
void print() { cout<<"Print m_nPower: "<<m_nPower<<endl; }
};
class Scanner : public virtual PoweredDevice
{
public:
Scanner(int nScanner, int nPower)
: PoweredDevice(nPower)
{
cout<<"Scanner: "<<nScanner<<endl;
}
};
class Printer : public virtual PoweredDevice
{
public:
Printer(int nPrinter, int nPower)
: PoweredDevice(nPower)
{
cout<<"Printer: "<<nPrinter<<endl;
}
};
class Copier : public Scanner, public Printer
{
public:
Copier(int nScanner, int nPrinter, int nPower)
:Scanner {nScanner, nPower}, Printer {nPrinter, nPower}, PoweredDevice {nPower}
{ }
};
int main()
{
Copier cCopier {1,2,3};
cCopier.print();
cout<<cCopier.m_nPower<<'\n';
return 0;
}
这似乎是一个 GCC 错误,当统一初始化与虚拟继承一起使用时触发。
如果我们改变:
Copier(int nScanner, int nPrinter, int nPower)
:Scanner {nScanner, nPower}, Printer {nPrinter, nPower}, PoweredDevice {nPower}
{ }
至:
Copier(int nScanner, int nPrinter, int nPower)
:Scanner (nScanner, nPower), Printer (nPrinter, nPower), PoweredDevice (nPower)
{ }
错误消失,并且按预期运行:
PoweredDevice: 3
Scanner: 1
Printer: 2
Print m_nPower: 3
3
Clang 和 Visual Studio 都能够正确编译原始代码,并给出预期的输出。