c++11 联合包含具有虚函数的数据成员
c++11 union contains data member with virtual function
#include <iostream>
class derive1{
public:
derive1() = default;
~derive1() = default;
virtual void func() { std::cout << "derive 1" << std::endl; }
};
class derive2 {
public:
derive2() = default;
~derive2() = default;
virtual void func() { std::cout << "derice 2" << std::endl; }
};
union classUnion {
classUnion() {};
~classUnion() {};
derive1 obj1;
derive2 obj2;
};
int main() {
classUnion u1;
u1.obj1.func(); // <-- OK print 'derive 1'
derive1 &dev1 = u1.obj1;
dev1.func(); // <-- OK print 'derive 1'
derive1 *ptr = &(u1.obj1);
ptr->func(); // <-- core dump/seg fault
return 0;
}
我认为 C++11 允许非平凡的构造函数(具有虚函数)。
我看不出这里有什么问题。
我用
"g++ -std=c+11 test.cpp" 编译它(gcc 4.8 和 gcc 5.0)。
问题是您从未在联合内初始化对象。至少,使其工作的最简单方法是以下小调整:
union classUnion {
classUnion() {};
~classUnion() {};
derive1 obj1={}; // unions can have one inline initializer
derive2 obj2;
};
但是,如果您改为这样做:
int main() {
classUnion u1;
u1.obj1 = derive1{};
...
}
还是会崩溃。原因是因为您要分配给一个未初始化的对象,特别是您有一个用户定义的析构函数(即虚拟析构函数)。
考虑以下因素:
(http://en.cppreference.com/w/cpp/language/union.)
If members of a union are classes with user-defined constructors and
destructors, to switch the active member, explicit destructor and
placement new are generally needed:
因此,要实际使用 类 虚函数(通常需要虚析构函数),您将需要使用 placement new 和手动销毁调用,如下所示:
int main() {
classUnion u1;
new (&u1.obj1) derive1{};
... // use obj1
u1.obj1.~derive1();
new (&u1.obj2) derive2{};
... // use obj2
u1.obj2.~derive2();
}
在您的示例中,None 对 func()
的调用是 OK,它们都是未定义的行为。 A union
不会默认初始化它的任何成员;如果要这样做,它会初始化哪一个?
为了演示这一点,将 non-static 数据成员添加到 derive1
并在 func()
中打印它,您将看到垃圾值,或者您的程序会更早崩溃。
class derive1{
public:
derive1() = default;
~derive1() = default;
virtual void func() { std::cout << "derive 1 " << i << std::endl; }
int i = 20;
};
要修复您的示例,请将 union
构造函数更改为在 mem-initializer-list
中构造 obj1
classUnion() : obj1() {};
或为 obj1
添加一个 brace-or-equal 初始值设定项
derive1 obj1 = {};
至于为什么前两次调用 func()
在你的例子中似乎有效,我猜 gcc 内联了那些函数调用,但在处理 [=21= 时它没有这样做], 导致最后一次调用失败。
#include <iostream>
class derive1{
public:
derive1() = default;
~derive1() = default;
virtual void func() { std::cout << "derive 1" << std::endl; }
};
class derive2 {
public:
derive2() = default;
~derive2() = default;
virtual void func() { std::cout << "derice 2" << std::endl; }
};
union classUnion {
classUnion() {};
~classUnion() {};
derive1 obj1;
derive2 obj2;
};
int main() {
classUnion u1;
u1.obj1.func(); // <-- OK print 'derive 1'
derive1 &dev1 = u1.obj1;
dev1.func(); // <-- OK print 'derive 1'
derive1 *ptr = &(u1.obj1);
ptr->func(); // <-- core dump/seg fault
return 0;
}
我认为 C++11 允许非平凡的构造函数(具有虚函数)。 我看不出这里有什么问题。 我用 "g++ -std=c+11 test.cpp" 编译它(gcc 4.8 和 gcc 5.0)。
问题是您从未在联合内初始化对象。至少,使其工作的最简单方法是以下小调整:
union classUnion {
classUnion() {};
~classUnion() {};
derive1 obj1={}; // unions can have one inline initializer
derive2 obj2;
};
但是,如果您改为这样做:
int main() {
classUnion u1;
u1.obj1 = derive1{};
...
}
还是会崩溃。原因是因为您要分配给一个未初始化的对象,特别是您有一个用户定义的析构函数(即虚拟析构函数)。
考虑以下因素: (http://en.cppreference.com/w/cpp/language/union.)
If members of a union are classes with user-defined constructors and destructors, to switch the active member, explicit destructor and placement new are generally needed:
因此,要实际使用 类 虚函数(通常需要虚析构函数),您将需要使用 placement new 和手动销毁调用,如下所示:
int main() {
classUnion u1;
new (&u1.obj1) derive1{};
... // use obj1
u1.obj1.~derive1();
new (&u1.obj2) derive2{};
... // use obj2
u1.obj2.~derive2();
}
None 对 func()
的调用是 OK,它们都是未定义的行为。 A union
不会默认初始化它的任何成员;如果要这样做,它会初始化哪一个?
为了演示这一点,将 non-static 数据成员添加到 derive1
并在 func()
中打印它,您将看到垃圾值,或者您的程序会更早崩溃。
class derive1{
public:
derive1() = default;
~derive1() = default;
virtual void func() { std::cout << "derive 1 " << i << std::endl; }
int i = 20;
};
要修复您的示例,请将 union
构造函数更改为在 mem-initializer-list
obj1
classUnion() : obj1() {};
或为 obj1
derive1 obj1 = {};
至于为什么前两次调用 func()
在你的例子中似乎有效,我猜 gcc 内联了那些函数调用,但在处理 [=21= 时它没有这样做], 导致最后一次调用失败。