使用unique_ptr的Singleton对象

时间:2018-03-21 09:54:58

标签: c++ c++11

我有以下CPP源代码,用于使用unique_ptr创建类的Singleton对象:

#include <iostream>
#include <memory>

class A
{

public:
    std::unique_ptr<A> getInstance(int log);
    ~A();

private:
    static bool instanceFlag;
    static std::unique_ptr<A> single;
    A(int log);
    int mLog;
};

bool A::instanceFlag = false;
std::unique_ptr<A> A::single = NULL;

std::unique_ptr<A> A::getInstance(int log)
{
    if(!instanceFlag)
    {
        //single = std::make_unique<A>(log);
        single = std::unique_ptr<A>(new A(log));
        instanceFlag = true;
        return std::move(single);
    }
    else
    {
        return std::move(single);
    }
}

A::A(int log) :
    mLog(log)
{

    std::cout << "Called A cons" << std::flush << std::endl;
}

int main()
{
std::unique_ptr<A> mA = A::getInstance(5);
}

但是当我编译代码时,我得到以下错误:

$ c++ -std=c++11 try2.cpp
try2.cpp: In function 'int main()':
try2.cpp:45:41: error: cannot call member function 'std::unique_ptr<A> A::getInstance(int)' without object
 std::unique_ptr<A> mA = A::getInstance(5);
                                         ^

但是我的项目中的代码格式完全一样,我收到错误:

Source code Line 39: single = std::make_unique<A>(log);

编译错误:

39:   required from here

single = std::make_unique<A>(log);

error: A(int log)' is private
 A::A(int log) :
  ^

4 个答案:

答案 0 :(得分:4)

首先是静态问题。这样:

std::unique_ptr<A> getInstance(int log);

是一个实例方法。您需要A的实例才能将其调用,但是您无法在不调用此方法的情况下获取A的实例,因此......

使用实例方法的原因是他们可以访问他们调用的实例。您的方法仅使用以下成员:

static bool instanceFlag;
static std::unique_ptr<A> single;

由于它们是静止的,因此不需要实例。只需将方法设置为静态,并且无需先从某处获取A即可调用它。

第二,逻辑问题。您的getInstance方法会向您的单个实例返回unique_ptrunique_ptr的全部内容是唯一:只有一个指针拥有并控制对象的生命周期。当您返回unique_ptr时,将单件对象的所有权从单身类本身转移给调用者。您甚至明确地调用move来明确表明这种情况正在发生。

现在,在调用getInstance一次之后,你的单身人士完全被打破了。如果再次调用getInstance,单身人士认为它有一个实例(因为instanceFlag),但unique_ptr处于不确定状态。我们唯一可以肯定的是,它没有控制A的实例。

只需将原始指针(或引用)返回A,而不是转移所有权。

答案 1 :(得分:3)

简短回答:不要使用单身人士。

答案很长:std::make_unique()使用它尝试创建的对象的构造函数,并且在您的代码中它是私有的。由于它是外部功能,因此不可能。您可以自己创建一个对象并将其传递给std::unique_ptr进行管理,就像您在注释对象下面的行一样。

最重要的问题是,当您执行std::move()时,您的班级成员single已不复存在。或者,更准确地说,处于未指定的状态。随着对getInstance的任何下一次通话,您都会通过......某事。您应该使用std::shared_ptr,您希望通过复制或返回引用返回std::unique_ptr

答案 2 :(得分:0)

问题在于,正如错误日志所示,您在没有任何实例的情况下调用member functiongetInstance。函数std::unique_ptr<A> getInstance(int log);应声明为static

声明static如下:

static std::unique_ptr<A> getInstance(int log);

注意:单例模式中 getInstance函数始终是静态函数。

以下是Wikipedia的摘录:

  

单例模式的实现必须:

     
      
  • 确保只存在单例类的一个实例;和
  •   
  • 提供对该实例的全局访问权。
  •   
     

通常,这可以通过以下方式完成:

     
      
  • 声明该类的所有构造函数都是私有的;和
  •   
  • 提供一个返回对实例的引用的静态方法。
  •   
     

实例通常存储为私有静态变量;在初始化变量时,在首次调用静态方法之前的某个时刻创建实例。

答案 3 :(得分:0)

即使在getInstance静态之后,它也不会编译,因为缺少析构函数的定义。给出空定义或保留默认值。

~A() = default;

移动single将首次运行并调用UB以便后续调用getInstance。而是通过引用&返回并删除std::move

static std::unique_ptr<A>& getInstance(int log);

std::unique_ptr<A>& A::getInstance(int log)
{
    if(!instanceFlag)
    {
        //single = std::make_unique<A>(log);
        single = std::unique_ptr<A>(new A(log));
        instanceFlag = true;
        return single;
    }
    else
    {
        return single;
    }
}

main()内,您可以通过引用获得 std::unique_ptr<A>& mA = A::getInstance(5);