【问题标题】:Static inline members initialization order静态内联成员初始化顺序
【发布时间】:2019-06-09 14:18:22
【问题描述】:

C++ 中一个众所周知的问题是static initialization order fiasco。当使用 C++17 静态内联成员时,它仍然被认为是一个问题吗?

这是一个示例,其中一个静态内联成员在两个不同的翻译单元(a.cpp 和 b.cpp)中用作两个非内联静态成员的初始化器:

counter.hh

#pragma once

#include <vector>
#include <fstream>

class Counter
{
    public:
        Counter()  { std::ofstream os("o.txt", std::ofstream::app); os << "Counter created" << std::endl; }
        ~Counter() { std::ofstream os("o.txt", std::ofstream::app); os << "Counter destroyed" << std::endl; }
        void add_instance()    
        { 
            ++m_instances; 
            std::ofstream os("o.txt", std::ofstream::app); os << "Counter increased: " << m_instances << std::endl; 
        }
        void remove_instance() 
        { 
            --m_instances; 
            std::ofstream os("o.txt", std::ofstream::app); os << "Counter decreased: " << m_instances << std::endl; 
        }

    private:
        int m_instances = 0;
};

class Object
{
    public:
        Object(Counter & counter) : m_counter(counter) 
        {
            m_counter.add_instance(); 
            std::ofstream os("o.txt", std::ofstream::app); os << "Object created" << std::endl; 
        }
        ~Object() 
        { 
            m_counter.remove_instance(); 
            std::ofstream os("o.txt", std::ofstream::app); os << "Object destroyed" << std::endl; 
        }

    private:
        Counter & m_counter;
};

struct C
{
    static inline Counter static_counter{};
};

a.hh

#pragma once

#include "counter.hh"

struct A
{
    static Object static_a; //not inline
};

a.cpp

#include "a.hh"

Object A::static_a{C::static_counter};

b.hh

#pragma once

#include "counter.hh"

struct B
{
    static Object static_b; //not inline
};

b.cpp

#include "b.hh"

Object B::static_b{C::static_counter};

ma​​in.cpp

#include "a.hh"
#include "b.hh"

int main() { }

输出(使用 MSVC 16.1.2)

Counter created
Counter increased: 1
Object created
Counter increased: 2
Object created
Counter decreased: 1
Object destroyed
Counter decreased: 0
Object destroyed
Counter destroyed

我认为,关于初始化,这种做法是安全的,因为 C++17 标准确保静态内联成员:(1) 始终在任何使用之前初始化,(2) 跨多个翻译单元仅初始化一次.

但我想知道这种模式是否存在任何隐藏的缺点,例如与跨不同 TU 的每个变量的破坏顺序有关。 static_astatic_b 总是在 static_counter 之前被销毁是否明确?

【问题讨论】:

  • 据我所知,毁灭的顺序,与创造的顺序相反。在这种情况下,这取决于您的包含顺序。 A.hpp,包含counter.hpp,所以初始化顺序为:Counter,A,B,销毁顺序为:B,A,Counter。我几乎可以肯定,所以让我们等待官方答复。
  • 也许你是对的,销毁顺序总是与创建顺序相反。但是不能保证顺序是 Counter,A,B。也可以是 Counter,B,A。它不依赖于包含顺序,因为我们有三个不同的翻译单元(a.cpp, b.cpp 和main.cpp),你怎么能说 main.cpp 中的变量优先于 a.cpp 中的变量?我认为它是由实现定义的,或者完全随机的。
  • 安全是什么意思? a/b 的值是不可预测的......
  • 我想了解的是,static_counter是否保证在static_a/static_b之前被初始化,是否保证在static_a/static_b之后被销毁。我知道static_astatic_b 如果用于初始化其他静态成员会很危险,但我只对static_counter 和其他依赖它进行构造的全局变量之间的关系感兴趣。也许我应该找到一个更清晰的例子

标签: c++ c++17 static-initialization


【解决方案1】:

是的,这很好,因为在每个翻译单元中 static_counterdefined before static_a/static_b。不保证破坏顺序是相反的(给定线程,这无论如何都是没有意义的),但是reverse of each guarantee holds,所以也可以。

【讨论】:

  • 谢谢,我想您指的是第一页中的段落 Dynamic Initalization - 2) 和第二页中的 1) - b) 段落页面。
  • @Tarquiscani:没错(如果有人关心的话,它是 C++11 之前的 1a 的析构函数)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-09-17
  • 1970-01-01
  • 2023-03-18
  • 1970-01-01
  • 2020-01-31
  • 1970-01-01
相关资源
最近更新 更多