单例模式的实现方式
📌想要实现一个单例设计模式,必须遵循以下规则:
- 私有的构造函数:防止外部代码直接创建类的实例
- 私有的静态实例变量:保存该类的唯一实例
- 公有的静态方法:通过公有的静态方法来获取类的实例
而在 C++ 中,推荐 静态局部变量的懒汉单例,简单且线程安全 (不需要锁、不需要静态实例变量):
class Single {
public:
static Single& GetInstance(); // 公有的静态函数,用于获取类的实例
private:
Single() = default; // 私有的构造函数,使用默认构造函数作为实现
};
Single& Single::GetInstance() {
static Single single; // 静态实例变量,保存该类的唯一实例
return single;
}
更加完整的版本(禁止外部拷贝构造和赋值操作):
class Single {
public:
static Single& GetInstance(); // 公有的静态函数,用于获取类的实例
private:
Single() = default; // 私有的构造函数,使用默认构造函数作为实现
~Single() = default;
Single(const Single &single) = delete; // 禁止外部拷贝构造
const Single &operator=(const Single &single) = delete; // 禁止外部赋值操作
};
Single& Single::GetInstance() {
static Single single; // 静态实例变量,保存该类的唯一实例
return single;
}
📌注意点:
- GetInstance 应当返回实例的引用,否则将产生副本
- 上面的实现中,没有使用私有的静态实例变量,而是利用了 C++ static 关键字,局部的 static Single single 的生命周期和程序运行周期相同
- 这是懒汉模式,因为只在第一次调用 GetInstance 时才会创建对象。
- 这种实现方式是线程安全的,参考 C++ 标准(
§6.7 [stmt.dcl] p4
):如果有多个线程同时进入变量的声明部分,也就是说,多个线程同时试图访问并初始化这个变量,那么当这个变量正在被初始化时,所有同时进入的线程会等待变量的初始化完成。
§6.7 [stmt.dcl] p4
If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.
单例模式练习
📚练习题:【设计模式专题之单例模式】1.小明的购物车 (kamacoder.com)
解答:
#include <iostream>
#include <string>
#include <sstream>
#include <map>
#include <vector>
using namespace std;
class Cart {
public:
static Cart& GetInstance(); // 公有的静态函数,用于获取类的实例
void addToCart(string name, int count);
void printCart();
private:
Cart() = default; // 私有的构造函数,使用默认构造函数作为实现
~Cart() = default;
Cart(const Cart &cart) = delete; // 禁止外部拷贝构造
const Cart &operator=(const Cart &cart) = delete; // 禁止外部赋值操作
vector<string> order; // 题目要求输出顺序与输入顺序相同,所以加一个order来记录
map<string, int> mp;
};
Cart& Cart::GetInstance() {
static Cart cart; // 静态实例变量,保存该类的唯一实例
return cart;
}
void Cart::addToCart(string name, int count) {
if (mp.find(name) != mp.end()) {
mp[name] += count;
} else {
order.push_back(name);
mp[name] = count;
}
}
void Cart::printCart() {
for (const string& name : order) {
cout << name << " " << mp[name] << endl;
}
}
int main() {
while(1) {
string input;
getline(cin, input);
if (input.empty()) break; // 空输入,跳出循环
stringstream ss(input);
string name;
int n;
ss >> name >> n;
Cart::GetInstance().addToCart(name, n);
}
Cart::GetInstance().printCart();
return 0;
}