使用unique_ptr的单一实例对象

Singleton object using unique_ptr

本文关键字:单一 实例 对象 ptr unique 使用      更新时间:2023-10-16

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

#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) :
^

首先,静态问题。这:

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,而不是转移所有权。

简短回答:不要使用单例。

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

最重要的问题是,当你做std::move()时,你的类成员single消失了。它变成空unique_ptr(又名。std::unique_ptr{},它不管理任何东西)。下次打电话给getInstance,你基本上是在返回nullptr。您应该使用std::shared_ptr,您希望通过复制或返回对std::unique_ptr的引用来返回它。

即使将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);

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

声明static如下:

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

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

以下是维基百科的摘录:

单一实例模式的实现必须:

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

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

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

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