#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)。
答案 0 :(得分:4)
问题是你永远不会初始化union中的对象。至少,使其成功的最简单方法是进行以下微调:
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。)
如果union的成员是具有用户定义的构造函数的类 析构函数,切换活动成员,显式析构函数和 通常需要新的位置:
因此,要真实地使用具有虚拟功能的类(通常需要虚拟析构函数),您将需要使用放置新的和手动的销毁调用,如下所示:
int main() {
classUnion u1;
new (&u1.obj1) derive1{};
... // use obj1
u1.obj1.~derive1();
new (&u1.obj2) derive2{};
... // use obj2
u1.obj2.~derive2();
}
答案 1 :(得分:2)
您示例中对func()
的调用都不是 OK ,它们都是未定义的行为。 union
不会默认初始化其任何成员;如果要这样做,它会初始化哪一个?
为了证明这一点,请将一个非静态数据成员添加到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内联了那些函数调用,但在处理derived1 *
时没有这样做,这导致最后一次通话失败。