c ++ 11 union包含具有虚函数的数据成员

时间:2023-01-15 17:52:50
#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;
}

I thought C++11 allow non-trivial constructor (with virtual function). I can't see what's the problem here. I use "g++ -std=c+11 test.cpp" to compile it (gcc 4.8 and gcc 5.0).

我认为C ++ 11允许非平凡的构造函数(具有虚函数)。我看不出这里有什么问题。我使用“g ++ -std = c + 11 test.cpp”来编译它(gcc 4.8和gcc 5.0)。

2 个解决方案

#1


4  

The problem is that you never initialize the object inside the union. At least, the easiest way to make it work is the following little tweak:

问题是你永远不会初始化union中的对象。至少,最简单的方法是进行以下微调:

union classUnion {
  classUnion() {};
  ~classUnion() {};
  derive1 obj1={}; // unions can have one inline initializer
  derive2 obj2;
};

However, if you instead do this:

但是,如果你这样做:

int main() {
  classUnion u1;
  u1.obj1 = derive1{};
  ...
}

It still will crash. The reason why is because you are assigning into an uninitialized object, and in particular you have a user defined destructor (i.e. a virtual one).

它仍然会崩溃。原因是因为您要分配到未初始化的对象,特别是您有一个用户定义的析构函数(即虚拟的)。

Consider the following: (http://en.cppreference.com/w/cpp/language/union.)

请考虑以下事项:(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:

如果union的成员是具有用户定义的构造函数和析构函数的类,则要切换活动成员,通常需要显式析构函数和placement new:

So to realistically use classes with virtual functions (which typically need virtual destructors), you're going to need to use placement new and manual destruction calls, like so:

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

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

#2


2  

None of the calls to func() in your example are OK, they're all undefined behavior. A union does not default initialize any of its members; if it were to do so, which one would it initialize?

您的示例中对func()的调用都没有,它们都是未定义的行为。 union不会默认初始化其任何成员;如果要这样做,它会初始化哪一个?

To demonstrate this, add a non-static data member to derive1 and print it within func(), you'll either see garbage values or your program will crash earlier.

为了演示这一点,将一个非静态数据成员添加到derive1并在func()中打印它,您将看到垃圾值或您的程序将更早崩溃。

class derive1{
  public:
    derive1() = default;
    ~derive1() = default;
    virtual void func() { std::cout << "derive 1 " << i << std::endl; }
    int i = 20;
};

Live demo

To fix your example, either change the union constructor to construct obj1 in the mem-initializer-list

要修复您的示例,请更改union构造函数以在mem-initializer-list中构造obj1

classUnion() : obj1() {};

or add a brace-or-equal initializer for obj1

或者为obj1添加一个大括号或者相等的初始值

 derive1 obj1 = {};

As for why the first 2 calls to func() in your example seemed to work, I'm guessing gcc inlined those function calls, but it didn't do so when dealing with the derived1 *, which caused the last call to fail.

至于为什么在你的例子中前两个调用func()似乎工作,我猜gcc内联那些函数调用,但它在处理derived1 *时没有这样做,这导致最后一次调用失败。

#1


4  

The problem is that you never initialize the object inside the union. At least, the easiest way to make it work is the following little tweak:

问题是你永远不会初始化union中的对象。至少,最简单的方法是进行以下微调:

union classUnion {
  classUnion() {};
  ~classUnion() {};
  derive1 obj1={}; // unions can have one inline initializer
  derive2 obj2;
};

However, if you instead do this:

但是,如果你这样做:

int main() {
  classUnion u1;
  u1.obj1 = derive1{};
  ...
}

It still will crash. The reason why is because you are assigning into an uninitialized object, and in particular you have a user defined destructor (i.e. a virtual one).

它仍然会崩溃。原因是因为您要分配到未初始化的对象,特别是您有一个用户定义的析构函数(即虚拟的)。

Consider the following: (http://en.cppreference.com/w/cpp/language/union.)

请考虑以下事项:(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:

如果union的成员是具有用户定义的构造函数和析构函数的类,则要切换活动成员,通常需要显式析构函数和placement new:

So to realistically use classes with virtual functions (which typically need virtual destructors), you're going to need to use placement new and manual destruction calls, like so:

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

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

#2


2  

None of the calls to func() in your example are OK, they're all undefined behavior. A union does not default initialize any of its members; if it were to do so, which one would it initialize?

您的示例中对func()的调用都没有,它们都是未定义的行为。 union不会默认初始化其任何成员;如果要这样做,它会初始化哪一个?

To demonstrate this, add a non-static data member to derive1 and print it within func(), you'll either see garbage values or your program will crash earlier.

为了演示这一点,将一个非静态数据成员添加到derive1并在func()中打印它,您将看到垃圾值或您的程序将更早崩溃。

class derive1{
  public:
    derive1() = default;
    ~derive1() = default;
    virtual void func() { std::cout << "derive 1 " << i << std::endl; }
    int i = 20;
};

Live demo

To fix your example, either change the union constructor to construct obj1 in the mem-initializer-list

要修复您的示例,请更改union构造函数以在mem-initializer-list中构造obj1

classUnion() : obj1() {};

or add a brace-or-equal initializer for obj1

或者为obj1添加一个大括号或者相等的初始值

 derive1 obj1 = {};

As for why the first 2 calls to func() in your example seemed to work, I'm guessing gcc inlined those function calls, but it didn't do so when dealing with the derived1 *, which caused the last call to fail.

至于为什么在你的例子中前两个调用func()似乎工作,我猜gcc内联那些函数调用,但它在处理derived1 *时没有这样做,这导致最后一次调用失败。