C++ 确保全局记录器对象比其他全局对象寿命更长

C++ 确保全局记录器对象比其他全局对象寿命更长,c++,singleton,global-variables,shared-ptr,C++,Singleton,Global Variables,Shared Ptr,我有一个带有一些单例对象的应用程序。他们希望在构造和销毁时写入日志消息,这发生在全局变量初始化时。现在,日志提供程序也是一个单例对象,如下所示: // loggingProvider.h class LoggingProvider { static boost::shared_ptr<LoggingProvider> instance; public: static boost::shared_ptr<LoggingProvider> getInstanc

我有一个带有一些单例对象的应用程序。他们希望在构造和销毁时写入日志消息,这发生在全局变量初始化时。现在,日志提供程序也是一个单例对象,如下所示:

// loggingProvider.h
class LoggingProvider {
    static boost::shared_ptr<LoggingProvider> instance;
public:
    static boost::shared_ptr<LoggingProvider> getInstance();
    /* ... */
};

// loggingProvider.cpp
boost::shared_ptr<LoggingProvider> LoggingProvider::instance;

boost::shared_ptr<LoggingProvider> getInstance() {
    if (!instance) {
        instance.reset(new LoggingProvider());
    }
    return instance;
}
// logger.h
class Logger {
    const boost::shared_ptr<LoggingProvider> provider;
    const std::string prefix;
public:
    Logger(const std::string prefix);
    /* ... */
}

// logger.cpp
Logger::Logger(const std::string& prefix) :
        provider(LoggingProvider::getInstance()), prefix(prefix) {}

遗憾的是,这段代码不起作用。当我调试它时,
LoggingProvider
的实例会被创建多次。我认为这是因为
实例
字段实际上是在一些记录器声明之后初始化的。现在我知道没有办法控制全局变量在文件中的初始化,那么有没有其他方法来实现这一点呢?

只需使用经典的单例习惯用法。你不想
shared\u ptr
在这里,因为您不想破坏 对象,永远。基本上是这样的:

class LoggingProvider
{
    static LoggingProvider* our_instance;
public:
    static LoggingProvider& instance();
    //  ...
};

LoggingProvider* LoggingProvider::our_instance
        = &LoggingProvider::instance();

LoggingProvider&
LoggingProvider::instance()
{
    if ( our_instance == NULL ) {
        our_instance = new LoggingProvider;
    }
    return *our_instance;
}
这里重要的是1)指针没有非平凡的 构造函数,2)实例永远不会被破坏

有一件事:因为您要输出到的任何文件都不会 关闭,确保刷新所有输出。(我通常是用电脑来做这件事 使用实际记录器的短期临时实例, 从
日志提供程序
获取目标streambuf,
然后在析构函数中刷新它。)

只需使用经典的单例习惯用法。你不想
shared\u ptr
在这里,因为您不想破坏 对象,永远。基本上是这样的:

class LoggingProvider
{
    static LoggingProvider* our_instance;
public:
    static LoggingProvider& instance();
    //  ...
};

LoggingProvider* LoggingProvider::our_instance
        = &LoggingProvider::instance();

LoggingProvider&
LoggingProvider::instance()
{
    if ( our_instance == NULL ) {
        our_instance = new LoggingProvider;
    }
    return *our_instance;
}
这里重要的是1)指针没有非平凡的 构造函数,2)实例永远不会被破坏

有一件事:因为您要输出到的任何文件都不会 关闭,确保刷新所有输出。(我通常是用电脑来做这件事 使用实际记录器的短期临时实例, 从
日志提供程序
获取目标streambuf,

并将其刷新到析构函数中。)

这是为什么Singleton是反模式的一个很好的例子。您可以考虑使用宏而不是单体来实现记录器:Logger@YZ.learner你的意思是一个很好的例子,说明为什么你需要一个单例,而不是使用其他东西。@JamesKanze我的意思是,IMHO,一个人应该尽量不使用任何单例,如果possible@YZ.learner当然,除非单身是合适的,或者在这种情况下,必要的。(当然,这里的名称可能有误导性。我们试图实现的是管理初始化顺序;对类的单个实例的限制是次要的。)这是一个很好的例子,说明了为什么Singleton是一种反模式。您可以考虑使用宏而不是单体来实现记录器:Logger@YZ.learner你的意思是一个很好的例子,说明为什么你需要一个单例,而不是使用其他东西。@JamesKanze我的意思是,IMHO,一个人应该尽量不使用任何单例,如果possible@YZ.learner当然,除非单身是合适的,或者在这种情况下,必要的。(当然,这里的名称可能有误导性。我们试图实现的是管理初始化顺序;仅对类的单个实例的限制是次要的。)但是,当应用程序在销毁其他单例对象之前退出时,可能会销毁此日志提供程序。如果该对象的析构函数想要记录某些内容,那么提供程序将已经不存在,因此记录器可能会出现segfault。我认为在多线程程序中仍然可能有多个LoggingProvider实例。线程A检查我们的_实例==NULL,线程B检查我们的_实例==NULL,然后他们都调用构造函数。我不认为多线程在这里是个问题。全局变量的初始化发生在创建多个线程之前。理论上,你可以用全局变量生成线程(谁会想要呢?),但这里不是这样。无论如何,用互斥锁保护getter不是问题。@flyx在第一次调用getter
LoggingProvider::instance()
之前,没有调用LoggingProvider的构造函数。因此,初始化时间对保护多实例问题没有任何作用。@flyx将不会破坏
LoggingProvider
。但是当应用程序在其他单例对象被销毁之前退出时,这个日志提供程序可能会被销毁。如果该对象的析构函数想要记录某些内容,那么提供程序将已经不存在,因此记录器可能会出现segfault。我认为在多线程程序中仍然可能有多个LoggingProvider实例。线程A检查我们的_实例==NULL,线程B检查我们的_实例==NULL,然后他们都调用构造函数。我不认为多线程在这里是个问题。全局变量的初始化发生在创建多个线程之前。理论上,你可以用全局变量生成线程(谁会想要呢?),但这里不是这样。无论如何,用互斥锁保护getter不是问题。@flyx在第一次调用getter
LoggingProvider::instance()
之前,没有调用LoggingProvider的构造函数。因此,初始化时间对保护多实例问题没有任何作用。@flyx将不会破坏
LoggingProvider
。曾经