C ++ unique_ptr导致应用程序崩溃

时间:2016-01-04 12:28:01

标签: c++ unique-ptr

我有一个带有一个静态函数的类。目的是为应用程序提供一个公共接口,以检索将记录到特定文件的记录器对象(假设,目前,文件不能由不同的文件路径表示法表示)。每个记录器对象都存储在一个带有相应文件名的映射中。如果在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错了吗?有没有更好的解决方案来实现我的意图?

编辑:我对这些问题有很多好的答案。最后我在我的代码中修改了Jarod的解决方案,但Joe的回答也非常有用。

3 个答案:

答案 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问题(静态实例的去初始化顺序)。