C++ 对局部静态变量的依赖

C++ Dependency on local static variable

提问人:ranstar74 提问时间:3/24/2023 更新时间:3/28/2023 访问量:87

问:

当调用重载的新运算符时,我的内存分配器被初始化:

MyAllocator* GetAllocator()
{
    static MyAllocator allocator;
    return &allocator;
}

void* operator new(size_t size)
{
    return GetAllocator()->Allocate(size);
}

构造分配器时,它会创建记录器。 在销毁时,它会打印出所有内存泄漏。

MyAllocator::MyAllocator()
{
    m_Logger = Logger::Create("Allocator.log");
    m_Logger->Writef("...");
}

MyAllocator::~MyAllocator()
{
    m_Logger->Writef("%u Leak(s) Total", m_LeakCount);
}

因为 Logger::Writef 使用本地静态缓冲区,所以我需要静态互斥锁。

Logger::Writef(const char* fmt, ...)
{
     static char buffer[4096];
     static Mutex mutex;

     mutex.Lock();
     ...
}

这样,我们得到以下初始化顺序:

  • GetAllocator::分配器
  • 记录器::Writef::mutex

破坏的顺序是相反的,这给了我们:

  • 记录器::Writef::mutex
  • GetAllocator::分配器

交易是这样的,当 Logger::Writef 从 MyAllocator 的析构函数调用时,Logger::Writef::mutex 已经被破坏了。

C++ 静态初始化

评论

2赞 πάντα ῥεῖ 3/24/2023
你的问题现在是?
2赞 NathanOliver 3/24/2023
Logger::Writef::mutex是一个函数局部静态,它将在程序的生命周期内存在。
1赞 ranstar74 3/24/2023
@NathanOliver我使用 IDA(使用 visual studio 2022 编译)反编译了我的项目,编译器为每个局部静态变量添加了“atexit”调用,它以 LIFO 顺序销毁对象。atexit 首先调用分配器实例,然后调用互斥锁。它们以相反的顺序被摧毁。
2赞 user17732522 3/24/2023
@ranstar74 如果调用 的构造函数,则销毁顺序应该是相反的。构造函数调用的完成很重要,而不是构造函数调用的开始。WritefMyAllocator
1赞 πάντα ῥεῖ 3/24/2023
@ranstar74额外要求的信息用于问题,而不是在评论中!

答:

0赞 Mark Ransom 3/28/2023 #1

你的问题很简单。它是由破坏的顺序引起的,所以你所要做的就是改变破坏的顺序。

正如你所指出的,破坏的顺序与建造的顺序相反。因此,您需要确保在分配器之前构造记录器变量。

为此,可以将静态记录器变量移出函数范围,并将它们放在模块范围中。它们将在程序启动时构建。通过将它们放在匿名命名空间中,它们不会暴露给其他模块。

namespace
{
     static char buffer[4096];
     static Mutex mutex;
}

Logger::Writef(const char* fmt, ...)
{
     mutex.Lock();
     ...
}