工会成员的析构函数似乎是自动调用的

Avi*_*ohn 3 c++ union destructor

我正在尝试实施标记联合。

我的理解是,在 C++ 联合中,非静态成员的非平凡(即非空)析构函数永远不会被调用,因此我们必须自己调用它们。这就是我所做的:

#include <iostream>

class C {
public:
  C() {
    std::cout << "C Ctor" << std::endl;
  }
  ~C() {
    std::cout << "C Dtor" << std::endl;
  }
};

class B {
public:
  B() {
    std::cout << "B Ctor" << std::endl;
  }
  ~B() {
    std::cout << "B Dtor" << std::endl;
  }
};

struct S {
  int type;

  union U {
    C c;
    B b;

    U() {

    }

    ~U() {}
  } u;

  S(int type) : type(type) {
    if (type == 0) {
      u.c = C();
    } else {
      u.b = B();
    }
  }

  ~S() {
    if (type == 0) {
      u.c.~C();
    } else {
      u.b.~B();
    }
  }
};

int main() {
  S s(0);
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

但是,输出是:

C Ctor
C Dtor
C Dtor
Run Code Online (Sandbox Code Playgroud)

意思是,C析构函数被调用了两次,而不是一次。

到底是怎么回事?如果您注意到我标记的联合实施的其他问题,请指出它们。

Nat*_*ica 6

S(int type) : type(type) {
    if (type == 0) {
      u.c = C();
    } else {
      u.b = B();
    }
  }
  
Run Code Online (Sandbox Code Playgroud)

由于您在构造u.c = C();函数的主体中,因此不是初始化而是赋值。这意味着您会看到调用了 的构造函数C(),然后在表达式的末尾调用了第一个析构函数调用来销毁该临时对象。我们可以通过添加来看到这一点

C& operator=(const C&) { std::cout << "operator=(const C&)\n"; return *this; }
Run Code Online (Sandbox Code Playgroud)

C这改变了输出到

C Ctor
operator=(const C&)
C Dtor
C Dtor
Run Code Online (Sandbox Code Playgroud)

然后第二个析构函数调用是s在 main 中超出范围并运行其析构函数时。


请注意,代码具有未定义的行为。联合不会激活构造函数用户提供的构造函数中的成员,因此当您这样做时

u.c = C();
Run Code Online (Sandbox Code Playgroud)

您正在分配给尚未存活的对象。您无法修改非活动对象。

  • @AvivCohn 据我所知,是的,激活不活跃的重要工会成员的唯一方法是安置新成员。 (4认同)
  • @AvivCohn 使用像 `S(int type) 这样的放置 `new` : type(type) { if (type == 0) { new (&amp;u.c) C(); } else { new (&amp;u.b) B(); } }`。这将为您提供“C Ctor C Dtor”作为输出。 (2认同)