访问类似singleton的静态成员的seg错误

seg fault for accessing a singleton-like static member

本文关键字:静态成员 错误 seg singleton 访问      更新时间:2023-10-16

这是简化的代码。。。

//A class used in the next class, Nothing much to worry about
class BasicLogger{
//...
};

下面是我的主要课。它有两个成员:一个是它自己类型的静态成员(称为log)。以及一个容器(称为repo),用于容纳上述类的对象。使用operator[]重载可以访问repo的项目

class Logger {
protected:
    //  repository of profilers. each profiler is distinguished by a file name!
    std::map<const std::string, boost::shared_ptr<BasicLogger> > repo; 
public:
    Logger(){} //breakpoints never reach here. why?
    //universal singleton-like access to this class
    static Logger log;
    //returns a member stored in the above 'repo' 
    virtual BasicLogger & operator[](const std::string &key);
};

问题来自于这种方法:

BasicLogger & Logger::operator[](const std::string &key)
{
    std::map<std::string, boost::shared_ptr<BasicLogger> >::iterator it = repo.find(key);
    if(it == repo.end()){
        std::cout << "creating a new Logger for " << key << std::endl;
        boost::shared_ptr<BasicLogger> t(new LogEngine(key));
        std::map<const std::string, boost::shared_ptr<BasicLogger> > repo_debug;//just for debug
        repo_debug.insert(std::make_pair(key,t));//ok
        repo.insert(std::make_pair(key,t));//seg fault
        return *t;
    }
    return *it->second;
}

最后一条信息:在整个项目中,访问repo容器中的项目如下。

namespace{
BasicLogger & logger = Logger::log["path_set"];
}

问题:

问题是,在程序开始时,在进行任何操作之前,控制直接进入BasicLogger & logger = Logger::log["path_set"];

Q1:为什么控制首先进入此处?仅仅因为log是静态的,或者最初也参与了匿名命名空间?

无论如何,因此当执行运算符[]时,repo似乎未初始化。我添加了一个具有与repo相同签名的局部伪变量(repo_debug)。并使用gdb:观察它们的值

//local repo_debug
    Details:{... _M_header = {... _M_parent = 0x0, _M_left = 0x7fffffffdc08, _M_right = 0x7fffffffdc08}, _M_node_count = 0}}}
//main 'repo'
    Details:{..._M_parent = 0x0, _M_left = 0x0, _M_right = 0x0}, _M_node_count = 0}}}

Q2。为什么repo未初始化?基本上,为什么不调用Logger的构造函数?

第三季度。非常感谢有关解决此问题的建议。感谢

Q1:假设声明在单独的编译单元中。编译单元之间的静态初始化顺序是由实现定义的。所以,这是因为偶然。我认为这是一个幸运的机会,因为从另一方面来说,你最初会认为它有效,但后来发现它在另一个cpu/编译器/os上坏了。

Q2:因为匿名命名空间中的logger首先被初始化,导致segfault阻止静态log初始化。

Q3.你可以通过在设计中避免单体来避免这个问题。但如果你想要singleton,避免静态初始化顺序失败的一种方法是构造第一次使用的习语:

Logger& Logger::log() {
   static Logger* log = new Logger();
   return *log;
}

缺点是动态分配的对象实际上从未被释放(无论如何,singleton都会在程序结束时被释放,但如果您在没有操作系统的情况下运行,这可能是一个问题)

静态局部初始化的线程安全性由§6.7/4(c++11草案)中的标准保证:

否则,这样的变量是在控件第一次通过其声明时初始化;这样的变量被认为是在完成其初始化。如果初始化通过抛出异常退出,则初始化未完成,因此将在下次控件进入声明时重试如果控制进入在初始化变量时并发声明,并发执行应等待初始化完成

在早期的标准和visual c++中,您可以通过确保至少在一个静态初始化期间调用的构造函数中调用log来避免并发问题(这发生在主程序生成任何线程之前)。