【问题标题】:Singleton being constructed twice单例被构建两次
【发布时间】:2014-02-14 17:16:02
【问题描述】:

我有以下代码:

#include <iostream>
#include <fstream>
#include <string>
#include <cassert>
#include <memory>

class Logger : public std::ofstream
{
public:
    explicit Logger(std::string const& filename = "log.txt"):
        std::ofstream(filename.c_str())
    {
        assert(*this);

        *this << "-- log file start --\n" << std::endl;
    }

    Logger::~Logger()
    {
        *this << "\n-- log file end --" << std::endl;

        this->close();
        this->clear();
    }
};

Logger logger;

template<class T>
class NotCopyable
{
public:

    NotCopyable() { }
    ~NotCopyable() { }

private:

    NotCopyable(NotCopyable const&);
    NotCopyable const& operator=(NotCopyable const&);
};

template<class T>
class Singleton : public NotCopyable<Singleton<T> >
{
public:

    static T& GetInstance()
    {
        if (!instance)
        {
            logger << "Initialize Singleton" << std::endl;

            instance.reset(new T());
        }

        return *instance;
    }

protected:

    Singleton() { }
    virtual ~Singleton() { }

private:

    static std::unique_ptr<T> instance;
};

template<class T>
std::unique_ptr<T> Singleton<T>::instance;

class Factory : public Singleton<Factory>
{
public:

    Factory() { logger << "Factory constructor" << std::endl; }
    ~Factory() { logger << "Factory destructor" << std::endl; }

    void Blargl() { logger << "Blargl" << std::endl; }

};

bool DoStuff()
{
    Factory::GetInstance().Blargl();

    return true;
}

bool Thingy = DoStuff();

int main(int, char*[])
{
    logger << "Start main()" << std::endl;

    Factory::GetInstance().Blargl();

    logger << "End main()" << std::endl;
}

这会输出以下内容:

-- log file start --

Initialize Singleton
Factory constructor
Blargl
Start main()
Initialize Singleton
Factory constructor
Blargl
End main()
Factory destructor

-- log file end --

我觉得自己很愚蠢,但不明白为什么要建造两次而不是一次。这是怎么回事?

【问题讨论】:

  • 原因 #57 为什么 Just Create One 击败了 Singleton。如果您只是使用全局变量,那么您已经完成了。
  • 我尝试运行您的代码,但没有重复问题。
  • 嗯。我正在使用 Visual Studio 2012(快捷版)。
  • 我在 Redhat 上使用 G++ 4.8.1。
  • !instanceinstance != nullptr 之间是否存在隐含的等价关系?

标签: c++ static singleton


【解决方案1】:

我尝试运行您的代码,并且在 OSX、Apple llvm 5.0 上遇到了您所描述的相同行为。
如果您在 GetInstance() 方法中定义静态实例变量,它就可以正常工作:

static T& GetInstance()
{
    static std::unique_ptr<T> instance
    if (!instance)
    {
        logger << "Initialize Singleton" << std::endl;

        instance.reset(new T());
    }

    return *instance;
}

我认为您在代码中遇到的问题是 Singleton::instance 在其声明点(默认构造函数)的初始化与 GetInstance() 方法中的赋值之间未指定的执行顺序。
所以,如果我说的是对的,那么执行的顺序大概是这样的:

  1. GetInstance() 来自DoStuff() 的电话
  2. Singleton&lt;Factory&gt;::instance 的默认构造
  3. 新的GetInstance() 内部调用main()

编辑:用以下代码测试了我的理论:

template <typename T>
class Ptr {
public:
    Ptr()
    : p() {
        std::cout << "Initalizing Ptr" << std::endl;
    }

    std::unique_ptr<T> p;
};



template<class T>
class Singleton : public NotCopyable<Singleton<T>> {
public:
    static T& GetInstance()
    {
        if (!instance.p)
        {
            std::cout << "Initalizing Singleton" << std::endl;
            logger << "Initialize Singleton" << std::endl;
            instance.p.reset(new T());
        }
    return *instance.p;
}

protected:
    Singleton() { }
    virtual ~Singleton() { }

private:
    static Ptr<T> instance;
};

template<class T>
Ptr<T> Singleton<T>::instance;

输出:

  1. 初始化单例
  2. 正在初始化指针
  3. 初始化单例

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-05-18
    • 2011-05-19
    • 2017-06-19
    • 1970-01-01
    • 2023-04-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多