【问题标题】:Is it safe not to destroy an object of a class type that is a member of a union?不销毁属于联合成员的类类型的对象是否安全?
【发布时间】:2021-10-22 09:34:17
【问题描述】:

我有这个例子:

struct A{
    A(){std::cout << "A's def-ctor\n";}
    ~A(){std::cout << "A's dtor\n";}
    A(A const&){std::cout << "A's copy-ctor\n";}
    A& operator = (A const&){std::cout << "A's copy-assign op\n"; return *this; }
};

struct Foo{
    Foo() : curMem_(INT), i_(0){}
    ~Foo(){
        if(curMem_ == CLS_A) // If I comment out this line then what happens?
            a_.~A();
    }
    enum {INT, CHAR, CLS_A, BOOL} curMem_;
    union{
        int i_;
        char c_;
        A a_;
        bool b_;
    };
};

Foo f;
f.curMem_ = Foo::CLS_A;
f.a_ = A();

f.curMem_ = Foo::BOOL;
f.b_ = true;
  • 我们知道类默认析构函数不知道类的联合类型成员的哪个成员是活动的,这就是我们需要定义析构函数的原因。所以联合的类类型的成员数据不会被自动销毁。那么如果我不显式调用联合的那些类类型成员的析构函数会发生什么?

  • 如果我在 Foo 析构函数中注释该行或删除析构函数本身会发生什么?是未定义的行为吗?

  • 我的类A 不通过原始指针管理资源,那么当它的对象是union 的成员时,为什么我还要费心显式调用它的析构函数?谢谢!

P.S:我有这个来自 C++ 入门第 5 版第 19.6 章联合:

我们的析构函数检查被销毁的对象是否包含一个字符串。如果是这样,析构函数显式调用字符串析构函数(第 19.1.2 节,第 824 页)以释放该字符串使用的内存。如果联合拥有任何内置类型的成员,则析构函数没有工作可做。

“如果联合拥有任何内置类型的成员,则析构函数没有工作可做。”我认为他可以添加:“或依赖于微不足道的析构函数的类类型”。你怎么看?

【问题讨论】:

  • 对于它的价值,您还应该考虑调用A 的析构函数,而a_ 在您分配给b_ 之前仍然是活动成员
  • @alterigel:这只是一个示例,以了解如果我不明确调用 dtor 会发生什么。我知道在一个真实的程序中我应该重载复制赋值运算符,并且在赋值之前我应该​​在 dtor 中检查它。
  • @ItachiUchiwa 换个角度想一想:“嘿,我写了我非常好的 RAII 类,现在有人把它放在 union 中,我应该担心吗?”
  • 如果 A 的析构函数没有被调用,那么效果就是 A 的析构函数没有被调用。这有多大的问题将完全取决于 A 的析构函数会做什么——例如如果 A 是 POD 类,则不会丢失任何有价值的东西,因为 A 的析构函数无论如何都是空操作。 OTOH 例如如果 A 持有一些 A 的析构函数应该释放的资源,那么你就永远泄露了该资源。 (注意即使 A 通过智能指针持有资源,泄漏仍然会发生,因为智能指针的析构函数不会被调用)
  • @JeremyFriesner:我认为这将是最好的答案。那么你会添加它作为答案吗?

标签: c++ class union destructor


【解决方案1】:

[basic.life]p6 中给出的标准的确切措辞是:

对于类类型的对象,在重用或释放对象占用的存储空间之前,程序不需要显式调用析构函数;但是,如果没有显式调用析构函数,或者如果没有使用删除表达式 ([expr.delete]) 来释放存储空间,则不会隐式调用析构函数并且 any依赖于析构函数产生的副作用的程序具有未定义的行为

(强调我的)

“取决于副作用”似乎很模糊,并且有很多关于堆栈溢出的问题都在讨论这个措辞。您的 A 类的析构函数似乎具有调用 I/O 函数的副作用,因此您似乎遇到了未定义的行为。

即使不是 UB,如果它是 std::vectorstd::stringstd::fstream,您也会泄漏内存或文件句柄等资源。这完全取决于类的析构函数(以及该类的任何成员)的作用。


由于“我的类A 不通过原始指针管理资源”,它应该真的有一个微不足道的析构函数。在这种情况下,这一点没有实际意义,不调用析构函数也没关系。

【讨论】:

  • 你的 A 类的析构函数似乎有调用 I/O 函数的副作用,所以你似乎遇到了未定义的行为。 但是,程序没有依赖这个副作用,所以不是UB。
猜你喜欢
  • 1970-01-01
  • 2023-03-03
  • 2013-07-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-27
相关资源
最近更新 更多