C++ C++;唯一\u ptr导致应用程序崩溃
我得到了一个只有一个静态函数的类。其目的是为应用程序提供一个公共接口,以检索将记录到特定文件的记录器对象(目前假定文件不能由不同的filepath符号表示)。每个记录器对象都存储在一个带有相应文件名的映射中。如果在C++ C++;唯一\u ptr导致应用程序崩溃,c++,unique-ptr,C++,Unique Ptr,我得到了一个只有一个静态函数的类。其目的是为应用程序提供一个公共接口,以检索将记录到特定文件的记录器对象(目前假定文件不能由不同的filepath符号表示)。每个记录器对象都存储在一个带有相应文件名的映射中。如果在config对象中再次传递相同的文件名,则返回旧的记录器,而不是创建新的记录器: typedef std::unique_ptr<AbstractLogger> LoggerPtr_t; typedef std::map<std::string, LoggerPtr_
config
对象中再次传递相同的文件名,则返回旧的记录器,而不是创建新的记录器:
typedef std::unique_ptr<AbstractLogger> LoggerPtr_t;
typedef std::map<std::string, LoggerPtr_t >::iterator LoggerMapIt_t;
std::map<std::string, LoggerPtr_t> LoggerFactory::mLoggerMap;
LoggerPtr_t LoggerFactory::getGenericLogger(const LoggerConfig& config){
std::string filename = config.getFileName();
LoggerMapIt_t itLogger = mLoggerMap.find(filename);
if(itLogger == mLoggerMap.end()){
mLoggerMap.insert(std::make_pair(filename, LoggerPtr_t(new SimpleLogger(config))));
itLogger = mLoggerMap.find(filename);
}
//if i uncommend the following 4 lines everything works fine
if(itLogger != mLoggerMap.end()){
return std::move(itLogger->second);
}
else
return LoggerPtr_t(new SimpleLogger(config));
}
typedef std::unique\u ptr LoggerPtr\t;
typedef std::map::迭代器LoggerMapIt\t;
std::map LoggerFactory::mLoggerMap;
LoggerPtr\u t LoggerFactory::getGenericLogger(const LoggerConfig&config){
std::string filename=config.getFileName();
LoggerMapIt\u t itLogger=mLoggerMap.find(文件名);
if(itLogger==mLoggerMap.end()){
insert(std::make_pair(文件名,LoggerPtr_t(新的SimpleLogger(配置)));
itLogger=mLoggerMap.find(文件名);
}
//如果我修复以下4行,一切正常
if(itLogger!=mLoggerMap.end()){
返回std::move(itLogger->second);
}
其他的
返回LoggerPtr_t(新的SimpleLogger(配置));
}
但是,如果不同的线程试图写入同一个记录器,应用程序似乎会崩溃。文件(表示std::ofstream
)受SimpleLogger
的log
方法中的互斥锁保护。我想原因就在于独特的\u ptr
。无论如何,我希望只有一个指向logger对象的指针,因为这个对象的行为就像一个唯一的元素(对于每个文件,只有一个logger)
应用程序崩溃是否可能是由唯一\u ptr
引起的?我是否使用了unique\u ptr
错误?是否有更好的解决方案来实现我的意图
编辑:我对这个问题有很多很好的答案。最后,我在代码中采用了Jarod的解决方案,但Joe的答案也非常有用 此行将从
std::map
if(itLogger != mLoggerMap.end()){
return std::move(itLogger->second);
}
因此,mLoggerMap
中itLogger
刚刚指向的unique\u ptr
现在是nullptr
。如果稍后再指向该元素,或从另一个线程指向该元素,尝试使用该唯一\u ptr
执行任何操作都会导致问题,因为您std::move
更早地移动指针
如果不想放弃指针的所有权,而只想访问指针,还可以更改函数的签名以返回底层原始指针
AbstractLogger* LoggerFactory::getGenericLogger(const LoggerConfig& config)
那么你可以说
if(itLogger != mLoggerMap.end()){
return itLogger->second.get();
}
否则,如果您想将所有权让与/转移给调用者,您应该从映射中删除元素,然后返回移动指针。当前,您转移所有权,因此映射包含空的
unique\u ptr
。因此,第二次调用检索空唯一\u ptr
由于您似乎不希望所有权转移,我将编写以下代码:
AbstractLogger& LoggerFactory::getGenericLogger(const LoggerConfig& config)
{
const std::string& filename = config.getFileName();
auto& logger = mLoggerMap[filename]; // insert (default) empty unique_ptr if not present.
if (logger == nullptr) {
logger = std::make_unique<SimpleLogger>(config);
}
return *logger;
}
AbstractLogger&LoggerFactory::getGenericLogger(const LoggerConfig&config)
{
const std::string&filename=config.getFileName();
auto&logger=mLoggerMap[filename];//如果不存在,则插入(默认)空的unique\u ptr。
如果(记录器==nullptr){
logger=std::使_唯一(配置);
}
返回*记录器;
}
这显示了如何使用std::shared\u ptr
(草稿,未测试):
使用LoggerPtr\u t=std::shared\u ptr;
使用LoggerMapIt\u t=std::map::迭代器;
std::map LoggerFactory::mLoggerMap;
LoggerPtr\u t LoggerFactory::getGenericLogger(const LoggerConfig&config){
//TODO:需要通过互斥来保护整个方法
std::string filename=config.getFileName();
LoggerMapIt\u t itLogger=mLoggerMap.find(文件名);
if(itLogger==mLoggerMap.end()){
LoggerPtr_t ptr(新SimpleLogger(配置));
插入(std::make_pair(文件名,ptr));
返回ptr;
}否则{
返回itLogger->second;
}
}
但是你真的应该完全检查Jarod42的答案。
关键是,您必须决定记录器对象的所有权
使用std::unique\u ptr
和引用(如Jarod42的答案所示)是更有效的代码,因为std::shared\u ptr
(甚至意味着“共享所有权”)比std::unique\u ptr
更昂贵
但另一方面,您必须注意静态初始化和去初始化,因为您的全局LoggerFactory::mLoggerMap
实例
您可以通过使用一个单例模式getter函数来解决这个问题(假设您甚至可以从其他全局实例中进行日志记录)。这也可能有助于解决应用程序shoutdown(静态实例的取消初始化顺序)问题。当使用新文件名调用getGenericLogger()时,创建一个新的SimpleLogger()对象并将其添加到映射中,然后再次创建一个新对象并返回该对象。在这种情况下,您可能希望返回第一个,而不是创建一个新的。我创建了一个新的Logger对象,将其添加到地图中,在地图中找到Logger,并使用move(itLogger->second)返回它。。。。至少这是我的本意。
记录器应该活多久?如果与出厂时一样长,则应返回指向AbstractLogger
的指针/引用。如果应该销毁对象的是方法的调用方,则返回一个unique\u ptr
。如果这是前一个案例中最长的时间,shared\u ptr
是合适的。在这种情况下,您使用的unique\u ptr
是错误的。是否考虑改用shared\u ptr
?或者,如果LoggerFactory::mLoggerMap
中的记录器实例永远不会被删除,您也可以决定
using LoggerPtr_t = std::shared_ptr<AbstractLogger>;
using LoggerMapIt_t = std::map<std::string, LoggerPtr_t>::iterator;
std::map<std::string, LoggerPtr_t> LoggerFactory::mLoggerMap;
LoggerPtr_t LoggerFactory::getGenericLogger(const LoggerConfig& config) {
// TODO: need to protect this whole method by a mutex
std::string filename = config.getFileName();
LoggerMapIt_t itLogger = mLoggerMap.find(filename);
if (itLogger == mLoggerMap.end()) {
LoggerPtr_t ptr(new SimpleLogger(config));
mLoggerMap.insert(std::make_pair(filename, ptr));
return ptr;
} else {
return itLogger->second;
}
}