我有一个带有一个静态函数的类。目的是为应用程序提供一个公共接口,以检索将记录到特定文件的记录器对象(假设,目前,文件不能由不同的文件路径表示法表示)。每个记录器对象都存储在一个带有相应文件名的映射中。如果在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));
}
但是,如果不同的线程尝试写入同一个记录器,应用程序似乎崩溃了。该文件(表示std::ofstream
)受log
SimpleLogger
方法中的互斥锁保护。我想unique_ptr
就是原因。无论如何,我想只有一个指向记录器对象的指针,因为这个对象的行为就像一个唯一的元素(对于每个文件只有一个记录器)。
应用程序崩溃是否可能由unique_ptr
引起?我使用unique_ptr
错了吗?有没有更好的解决方案来实现我的意图?
答案 0 :(得分:3)
此行将&#34;采取&#34;来自this.ws.onclose = function() {
var msg;
msg = "Whoops! Lost connection to " + _this.ws.url;
if (typeof _this.debug === "function") {
_this.debug(msg);
}
_this._cleanUp();
return typeof errorCallback === "function" ? errorCallback(msg) : void 0;
};
return this.ws.onopen = function() {
if (typeof _this.debug === "function") {
_this.debug('Web Socket Opened...');
}
headers["accept-version"] = Stomp.VERSIONS.supportedVersions();
headers["heart-beat"] = [_this.heartbeat.outgoing, _this.heartbeat.incoming].join(',');
return _this._transmit("CONNECT", headers);
};
unique_ptr
std::map
因此,if(itLogger != mLoggerMap.end()){
return std::move(itLogger->second);
}
unique_ptr
所指向的mLoggerMap
现在是itLogger
。如果您稍后或从另一个线程指向该元素,尝试对该nullptr
执行任何操作都会导致问题,因为您先前unique_ptr
指针。
如果您不想要放弃指针的所有权,而只是想要访问指针,那么您还可以更改函数的签名以返回基础原始指针
std::move
然后你可以说
AbstractLogger* LoggerFactory::getGenericLogger(const LoggerConfig& config)
否则,如果您执行想要放弃/转让所有权给调用者,您应该从地图中删除该元素,然后返回移动指针。
答案 1 :(得分:2)
目前,您转移了所有权,因此地图后面包含空unique_ptr
。所以第二次调用检索空unique_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;
}
答案 2 :(得分:1)
这表明如何使用std::shared_ptr
(草稿,未经测试):
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;
}
}
但你真的应该完全检查Jarod42的答案。 关键是,您必须决定记录器对象的所有权。
使用std::unique_ptr
和引用(如Jarod42及其答案中所示)是更有效的代码,因为std::shared_ptr
(甚至意味着&#34;共享所有权&#34;)更贵比std::unique_ptr
。
但另一方面,由于您的全局LoggerFactory::mLoggerMap
实例,您必须注意静态初始化和取消初始化。
您可以通过使用例如单例模式getter函数(假设您甚至可以从其他全局实例c&#39; tors中记录)。这也可能有助于解决应用程序shoutdown问题(静态实例的去初始化顺序)。