当构造函数不存在时,析构函数被省略?

问题描述 投票:0回答:1

下面的代码重现了我遇到的问题,MSVC 2022:

#include <iostream>

struct A {
    static void message()
    {
        std::cout << "A::message()\n";
    }

    struct B {
        B() {}
        ~B()
        {
            A::message();
        }
    };

    static inline B b;
};
 
int main()
{
}

通过上面的代码,我看到如预期的那样出现了消息。然而:

  1. 如果我删除构造函数
    B()
    ,那么析构函数
    ~B()
    似乎 由于消息消失而被删除。
  2. 如果我随后添加一个虚拟属性
    int dummy;
    到结构
    B
    ,则该消息会再次出现。

我实在找不到答案的问题:

  1. 在我看来,如果没有构造函数
    B()
    并且
    B
    中没有属性,编译器只是将类
    B
    标记为空,然后完全跳过它。这是正确的吗?
  2. 有记录表明这种情况可能发生吗?鉴于内容 析构函数的我认为这永远不应该发生。
  3. 我怎样才能保证它总是正确工作,而不仅仅是对于我正在使用的编译器 现在使用。定义构造函数
    B()
    是否足够,或者我还应该使用虚拟属性
    B
    使
    int dummy;
    非空吗?
c++ constructor destructor
1个回答
0
投票

区别在于

b
是否有静态初始化

如果静态存储持续时间变量具有静态初始化,则它会在任何其他没有静态初始化的静态存储持续时间变量之前初始化(即动态初始化)。

特别是,

<iostream>
中定义了另一个重要的静态存储持续时间对象,即
std::ios_base::Init
的实例。该类型对象的初始化会导致标准 IO 流的初始化(例如
std::cout
)。当它的最后一个实例被销毁时,它还负责刷新所有这些流。

因此,为了查看您的输出,您必须确保

std::ios_base::Init
对象在您的b之后
被摧毁。然而,销毁的顺序与初始化的顺序相反。因此,如果 
b
 具有静态初始化,但 
std::ios_base::Init
 对象具有动态初始化,那么这是错误的方法。

一般来说,如果静态存储持续时间变量的初始化是常量表达式,则该变量具有静态初始化。如果您不声明任何构造函数,就是这种情况。

如果初始化不是常量表达式,那么它通常具有动态初始化,只不过在许多情况下实现可以自由选择静态初始化。如果您使用非

constexpr

 构造函数或未初始化成员,则会遇到这种情况。

因此,为了可靠地获得正确的顺序,您需要确保

b

 没有静态初始化。由于实施上有余地,这有点棘手。做一些显然只能在运行时发生的事情应该足够了(例如调用一些 IO),但标准当前指定的方式,很难完全确定。 (请参阅
CWG 第 1294 期。)

更好的选择可能是添加一个

std::ios_base::Init

 对象作为 
B
 的成员,这样当 
b
 的析构函数运行时,这样一个对象肯定会保持活动状态。

© www.soinside.com 2019 - 2024. All rights reserved.