提问者:小点点

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


我正在尝试实现一个标记工会。

我的理解是,在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析构函数将被调用两次,而不是仅调用一次。

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


共2个答案

匿名用户

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();

该文件将被复制,然后被销毁。 因此,输出的前2行属于这个实例。 最后一个输出行是~s()调用的结果。

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