单例模式

单例模式能够保证一个类只有一个实例, 并提供一个访问该实例的全局节点,所以任何用户在该实例创建之后将不再允许再次创建。

单例模式有多种实现方式,当我们要实现的单例模式必须是线程安全,且派生类通过继承就可以得到轻松创建该派生类的单例模式,后续就不必在每个类中都去实现一份单例了。

  • once_flag 和 call_once 保证创建单例的方法在多线程环境下只会被调用一次
  • static 关键字 保证 实例只能拥有一份(但需要设计才能保证多线程环境下独一份)
  • 用智能指针管理单例的实例对象
  • 禁用拷贝构造函数和拷贝赋值运算符函数,保证对象不可被拷贝构造和拷贝赋值,但是由于我们这里要设计的这个单例类用以被继承,所以得是 protected。派生类以 public 继承之后,自然会让其成为 private
  • 单例类的构造函数必须是 private,这样才能将类的创建权控制在类的内部,但是由于我们这里要设计的这个单例类用以被继承,所以得是 protected。派生类以 public 继承之后,自然会让其成为 private
  • 单例类的析构函数必须是 private,保证对象不可以在外部随意删除,但是由于我们这里要设计的这个单例类用以被继承,所以得是 protected。派生类以 public 继承之后,自然会让其成为 private
  • 既然希望创建任意类的单例,需要用到模板类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
template <typename T>
class Singleton {
protected:
Singleton() = default;
~Singleton() = default;

Singleton(const Singleton<T>&) = delete;
Singleton& operator=(const Singleton<T>&) = delete;

static std::shared_ptr<T> instance_;
static std::once_flag once_;
public:
static std::shared_ptr<T> getInstance() {
std::call_once(once_, []() {
instance_ = std::make_shared<T>();
});
return instance_;
}
};

template <typename T>
std::shared_ptr<T> Singleton<T>::instance_ = nullptr;

template <typename T>
std::once_flag Singleton<T>::once_;

客户端测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Student : public Singleton<Student> {
friend class Singleton<Student>; // 将 Singleton<Student> 声明为友元类,其私有的构造函数和析构函数都可以被 Singleton 调用了
public:
void doSomething() {
std::cout << " Student do" << std::endl;
}
};

void testSingleton() {
std::shared_ptr<Student> instance1 = Student::getInstance();
std::shared_ptr<Student> instance2 = Student::getInstance();

if (instance1 == instance2) {
std::cout << "Singleton test passed: Both instances are the same." << std::endl;
}
else {
std::cout << "Singleton test failed: Instances are different." << std::endl;
}

instance1->doSomething();
}

int main() {

std::thread t1(testSingleton);
std::thread t2(testSingleton);

t1.join();
t2.join();

return 0;
}
/*
Singleton test passed: Both instances are the same.
Student do
Singleton test passed: Both instances are the same.
Student do
*/