联合成员的析构函数似乎被自动调用

Destructor of union member seems to be called automatically

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

我的理解是,在 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;
}

然而,输出是:

C Ctor
C Dtor
C Dtor

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

这是怎么回事?如果您发现我的标记联合实施存在其他问题,请指出。

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

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

看到这一点
C& operator=(const C&) { std::cout << "operator=(const C&)\n"; return *this; }

C 将输出更改为

C Ctor
operator=(const C&)
C Dtor
C Dtor

然后第二次析构函数调用是当 s 超出 main 的范围时,它的析构函数是 运行.


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

u.c = C();

您正在分配给一个尚未激活的对象。您无法修改不存在的对象。

在构造函数中创建 C:

的临时实例
u.c = C();

复制然后销毁。所以,前两行输出属于这个实例。 最后一行输出是您调用 ~S() 的结果。

除此之外,从 C++17 开始,您拥有标记联合的标准强大实现:https://en.cppreference.com/w/cpp/utility/variant