C++11 联合包含具有虚函数的数据成员

c++11 union contains data member with virtual function

本文关键字:函数 数据成员 包含具 C++11      更新时间:2023-10-16
#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。

如果联合的成员是具有用户定义的构造函数的类,并且 析构函数,用于切换活动成员、显式析构函数和 通常需要新的放置:

因此,要实际使用具有虚函数(通常需要虚拟析构函数(的类,您将需要使用放置新调用和手动销毁调用,如下所示:

int main() {
  classUnion u1;
  new (&u1.obj1) derive1{};
  ... // use obj1
  u1.obj1.~derive1();
  new (&u1.obj2) derive2{};
  ... // use obj2
  u1.obj2.~derive2();
}
示例中对

func()的调用都不是正常的,它们都是未定义的行为。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 初始值设定项列表中构造obj1

classUnion() : obj1() {};

obj1添加大括号或等于初始值设定项

 derive1 obj1 = {};

至于为什么在您的示例中对func()的前 2 次调用似乎有效,我猜 gcc 内联了这些函数调用,但在处理 derived1 * 时没有这样做,这导致最后一次调用失败。

相关文章: