设计一个只有一个可能的实例(C++)的类/类型

Design a class/type with only 1 possible instance - C++

本文关键字:C++ 实例 的类 类型 有一个 一个      更新时间:2023-10-16

我想要一个class T,它在整个程序中只能生成一个实例。

现在我知道了std::unique_ptr,但有两个问题:

  • 它被限制在一个范围内(但这不是一个大问题…)
  • 它需要显式使用,这意味着它不是类或类型的一部分,它只是一个处理程序和一个特殊的指针,但它不会修改我的类的设计

现在,我希望class T的设计方式即使是错误的,用户也可以在同一程序中声明两个实例,并且我不能依赖于我的用户将为T声明std::unique_ptr的事实,因为我想通过设计来解决这个问题。

现在我只是在想如何以优雅的方式隐含地使用unique_ptr,问题是我现在没有任何线索。

另一种方法是检查这个类是否由unique_ptr处理,但这种检查会使我在性能方面失去优势。

由于只有一个实例真的很重要,所以在我的情况下,我只看到两个选项:1)试图通过设计解决这个问题2)在编译时使用某种检查/宏抛出错误。

我知道这看起来微不足道,但从设计方法来看,至少对我来说不是,所以请帮忙。

您正在寻找的被称为Singleton模式,虽然它被许多人(包括我自己)广泛认为是一种反模式,但我仍将向您展示构建一个模式所需的基本元素。

基本上,你需要做的是提供三件事:

  1. 一个"获取"唯一实例的static方法
  2. private构造函数,因此没有人可以实例化它
  3. (可选)在main启动之前创建唯一实例的方法

以下是基本代码:

class Singleton
{
public:
Singleton&  get() 
{ 
static Singleton me_;
return me_;
}
private:
Singleton() {};
};

我让您来了解如何实现上面的#3,以及为什么不应该首先使用Singleton——原因有很多。

这通常被称为Singleton。

请参阅http://en.wikipedia.org/wiki/Singleton_pattern

C++中的典型技巧是使用一个函数,该函数通过引用返回singleton实例,并使构造函数私有。

类似于:

#include <iostream>
using namespace std;
class Foo
{
private:
Foo() : a(3) { a++; }
static Foo singleton;
int a;
public:
static Foo& getFoo() { return singleton; }
void doStuff() { cout<<"My a is: "<<a<<endl; }
};
Foo Foo::singleton;
int main(int argc, char** argv)
{
Foo::getFoo().doStuff();
Foo &foo = Foo::getFoo();
foo.doStuff();
//uncomment below to cause compile error                                                                                                                                                                                                                                                                                                                                
//Foo foo2;                                                                                                                                                                                                                                                                                                                                                             
}

请注意,在实际代码中,您将把它拆分为一个标头和一个cpp文件。在这种情况下,

Foo Foo::singleton;

零件必须放在cpp文件中。

您至少可以拥有

static int count;
assert(count == 0);
count++;

在singleton类的构造函数中。这并不能确保在编译时您的类是单例的,但至少它在运行时会检查这一点。

您也可以将构造函数设为私有的,并让一个静态成员函数(一次)返回一个指向实例的指针,可能类似于

class Singleton {
private:
Singleton() {
static int count;
assert(count == 0);
count++;
};
Singleton(Singleton&) = delete;
public:
static Singleton* the_instance() {
static Singleton* it;
if (!it) it = new Singleton();
return it;
}
};