构建虚拟基础时的编译器行为 class
Compiler behaviour when constructing virtual base class
考虑这段代码:
#include <iostream>
class A {
public:
A(int s) { std::cout << "A(" << s << ")\n"; }
};
class B1 : virtual public A {
public:
B1(int s1, int s2)
: A{s1} { std::cout << "B1(" << s1 << "," << s2 << ")\n"; }
};
class B2 : virtual public A {
public:
B2(int s1, int s2)
: A{s1} { std::cout << "B2(" << s1 << "," << s2 << ")\n"; }
};
class C1 : public B1, public B2 {
public:
C1() : B1{1,2}, B2{3,4}, A{5} {}
};
class C2 : public B1, public B2 {
public:
C2() : B1(1,2), B2(3,4), A{5} {}
};
int main()
{
std::cout << "Create c1:\n";
C1 c1;
std::cout << "\n";
std::cout << "Create c2:\n";
C2 c2;
}
Class A 是 B1 和 B2 的虚拟基 class。 Classes C1和C2完全相同,只是C1使用{...}
,C2使用(...)
构造B1和B2。
由于这里使用了虚拟继承,classA应该作为C1或C2构造的一部分来构造。
如果我使用 Microsoft VS2015 编译此代码,它会在 运行:
时产生此输出
Create c1:
A(5)
B1(1,2)
B2(3,4)
Create c2:
A(5)
B1(1,2)
B2(3,4)
这正是我所期望的。
但是如果我用 GCC (6.1.0) 编译它会产生这个输出:
Create c1:
A(5)
A(1)
B1(1,2)
A(3)
B2(3,4)
Create c2:
A(5)
B1(1,2)
B2(3,4)
这里A的构造函数在构造c1的时候调用了3次,在构造c2的时候只调用了一次。
这是 GCC 中的错误还是我误解了什么?
不太清楚为什么会这样,但 A class 不应该是多态的(至少有一个虚函数)以正确地虚拟继承它吗?!
这个bug更严重:
#include <iostream>
class A {
public:
A(int s) { std::cout << "A(" << s << ")\n"; ms = s; };
int ms;
virtual void dummy() {std::cout << "Aaaaaa!" << std::endl;};
virtual ~A() {std::cout << "~A(" << ms << ")\n";};
};
class B1 : virtual public A {
public:
B1(int s1, int s2)
: A{s1} { std::cout << "B1(" << s1 << "," << s2 << ")\n"; };
};
class B2 : virtual public A {
public:
B2(int s1, int s2)
: A{s1} { std::cout << "B2(" << s1 << "," << s2 << ")\n"; };
};
class C1 : public B1, public B2 {
public:
C1() : B1{1,2}, B2{3,4}, A{5} {};
};
class C2 : public B1, public B2 {
public:
C2() : B1(1,2), B2(3,4), A{5} {};
};
int main()
{
{
std::cout << "Create c1:\n";
C1 c1;
std::cout << "Calling A's dummy on c1: " << std::endl;
c1.dummy();
std::cout << "Size of c1: " << sizeof(c1) << std::endl;
}
{
std::cout << "\n";
std::cout << "Create c2:\n";
C2 c2;
std::cout << "Calling A's dummy on c2: " << std::endl;
c2.dummy();
std::cout << "Size of c2: " <<sizeof(c2) << std::endl;
}
}
在 GCC 5.4.0 上编译
运行 此代码产生以下输出:
- 创建 c1:
- A(5)
- A(1)
- B1(1,2)
- A(3)
- B2(3,4)
- 在 c1 上调用 A 的虚拟对象:
- 啊啊啊!
- c1 的大小:32
- ~A(3)
- 创建c2:
- A(5)
- B1(1,2)
- B2(3,4)
- 在 c2 上调用 A 的虚拟对象:
- 啊啊啊!
- c2 的大小:32
- ~A(5)
回答我自己的问题:
显然,GCC 在这种情况下确实存在错误。使用 GCC 版本 7.0.0 编译代码会产生正确的行为,输出如下:
Create c1:
A(5)
B1(1,2)
B2(3,4)
Create c2:
A(5)
B1(1,2)
B2(3,4)
考虑这段代码:
#include <iostream>
class A {
public:
A(int s) { std::cout << "A(" << s << ")\n"; }
};
class B1 : virtual public A {
public:
B1(int s1, int s2)
: A{s1} { std::cout << "B1(" << s1 << "," << s2 << ")\n"; }
};
class B2 : virtual public A {
public:
B2(int s1, int s2)
: A{s1} { std::cout << "B2(" << s1 << "," << s2 << ")\n"; }
};
class C1 : public B1, public B2 {
public:
C1() : B1{1,2}, B2{3,4}, A{5} {}
};
class C2 : public B1, public B2 {
public:
C2() : B1(1,2), B2(3,4), A{5} {}
};
int main()
{
std::cout << "Create c1:\n";
C1 c1;
std::cout << "\n";
std::cout << "Create c2:\n";
C2 c2;
}
Class A 是 B1 和 B2 的虚拟基 class。 Classes C1和C2完全相同,只是C1使用{...}
,C2使用(...)
构造B1和B2。
由于这里使用了虚拟继承,classA应该作为C1或C2构造的一部分来构造。
如果我使用 Microsoft VS2015 编译此代码,它会在 运行:
时产生此输出Create c1:
A(5)
B1(1,2)
B2(3,4)
Create c2:
A(5)
B1(1,2)
B2(3,4)
这正是我所期望的。
但是如果我用 GCC (6.1.0) 编译它会产生这个输出:
Create c1:
A(5)
A(1)
B1(1,2)
A(3)
B2(3,4)
Create c2:
A(5)
B1(1,2)
B2(3,4)
这里A的构造函数在构造c1的时候调用了3次,在构造c2的时候只调用了一次。
这是 GCC 中的错误还是我误解了什么?
不太清楚为什么会这样,但 A class 不应该是多态的(至少有一个虚函数)以正确地虚拟继承它吗?!
这个bug更严重:
#include <iostream>
class A {
public:
A(int s) { std::cout << "A(" << s << ")\n"; ms = s; };
int ms;
virtual void dummy() {std::cout << "Aaaaaa!" << std::endl;};
virtual ~A() {std::cout << "~A(" << ms << ")\n";};
};
class B1 : virtual public A {
public:
B1(int s1, int s2)
: A{s1} { std::cout << "B1(" << s1 << "," << s2 << ")\n"; };
};
class B2 : virtual public A {
public:
B2(int s1, int s2)
: A{s1} { std::cout << "B2(" << s1 << "," << s2 << ")\n"; };
};
class C1 : public B1, public B2 {
public:
C1() : B1{1,2}, B2{3,4}, A{5} {};
};
class C2 : public B1, public B2 {
public:
C2() : B1(1,2), B2(3,4), A{5} {};
};
int main()
{
{
std::cout << "Create c1:\n";
C1 c1;
std::cout << "Calling A's dummy on c1: " << std::endl;
c1.dummy();
std::cout << "Size of c1: " << sizeof(c1) << std::endl;
}
{
std::cout << "\n";
std::cout << "Create c2:\n";
C2 c2;
std::cout << "Calling A's dummy on c2: " << std::endl;
c2.dummy();
std::cout << "Size of c2: " <<sizeof(c2) << std::endl;
}
}
在 GCC 5.4.0 上编译 运行 此代码产生以下输出:
- 创建 c1:
- A(5)
- A(1)
- B1(1,2)
- A(3)
- B2(3,4)
- 在 c1 上调用 A 的虚拟对象:
- 啊啊啊!
- c1 的大小:32
- ~A(3)
- 创建c2:
- A(5)
- B1(1,2)
- B2(3,4)
- 在 c2 上调用 A 的虚拟对象:
- 啊啊啊!
- c2 的大小:32
- ~A(5)
回答我自己的问题:
显然,GCC 在这种情况下确实存在错误。使用 GCC 版本 7.0.0 编译代码会产生正确的行为,输出如下:
Create c1:
A(5)
B1(1,2)
B2(3,4)
Create c2:
A(5)
B1(1,2)
B2(3,4)