gpt4 book ai didi

c++ - 访问类似单例的静态成员的段错误

转载 作者:太空宇宙 更新时间:2023-11-04 11:27:39 24 4
gpt4 key购买 nike

这是简化后的代码...

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

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

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"];

问题 1: 为什么控件首先出现在此处?仅仅因为 log 是静态的还是匿名命名空间最初也被参与?

无论如何,所以当 operator[] 被执行时,repo 似乎是未初始化的。我添加了一个本地虚拟变量 (repo_debug),其签名与 repo 相同。并使用 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 的构造函数?

Q3. 非常感谢解决此问题的建议。谢谢

最佳答案

Q1:大概声明在单独的编译单元中。跨编译单元的静态初始化顺序是实现定义的。所以,它是偶然的。我会说幸运的机会,因为另一种方式你最初会认为它只是工作,后来发现它在另一个 cpu/编译器/操作系统上中断。

Q2:因为匿名命名空间中的 logger 首先被初始化,导致了一个段错误,阻止了静态 log 的初始化。

Q3。您可以通过在设计中避免单例来避免该问题。但是如果你想要单例,避免静态初始化顺序失败的一种方法是 Construct On First Use Idiom:

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

缺点是动态分配的对象永远不会真正释放(单例无论如何都会在程序结束时被释放,但如果你在没有操作系统的情况下运行可能会出现问题)

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

...Otherwise such a variable is initialized the first time control passes through its declaration; such a variable is considered initialized upon the completion of its initialization. If the initialization exits by throwing an exception, the initialization is not complete, so it will be tried again the next time control enters the declaration. If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.

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

关于c++ - 访问类似单例的静态成员的段错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26055092/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com