题目描述:
小明去了一家大型商场,拿到了一个购物车,并开始购物。请你设计一个购物车管理器,记录商品添加到购物车的信息(商品名称和购买数量),并在购买结束后打印出商品清单。(在整个购物过程中,小明只有一个购物车实例存在)。
输入描述:
输入包含若干行,每行包含两部分信息,分别是商品名称和购买数量。商品名称和购买数量之间用空格隔开。
输出描述:
输出包含小明购物车中的所有商品及其购买数量。每行输出一种商品的信息,格式为 "商品名称 购买数量"。
本道题目请使用单例设计模式:
使用私有静态变量来保存购物车实例。
使用私有构造函数防止外部直接实例化。
我们来看代码:
#include<iostream>
#include<unordered_map>
#include<vector>
#include<string>
using namespace std;
class ShoppingCartManager
{
private:
unordered_map<string, int>cart;
vector<string>order;
//私有构造函数
ShoppingCartManager(){}
public:
//获取购物车实例
static ShoppingCartManager* getInstance()
{
static ShoppingCartManager Instance;
return &Instance;
}
//添加商品
void addToCart(const string& itemName, int quantity)
{
if (cart.find(itemName) == cart.end())//如果之前没买过
{
order.emplace_back(itemName);
}
cart[itemName] += quantity;
}
//查看商品
void viewCart()const
{
for (const auto& itemName : order)
{
cout << itemName << " " << cart.at(itemName) << endl;
}
}
};
int main()
{
string itemName;
int quantity;
ShoppingCartManager* cart = ShoppingCartManager::getInstance();
while (cin >> itemName >> quantity)
{
cart->addToCart(itemName, quantity);
}
cart->viewCart();
return 0;
}
上述代码的static ShoppingCartManager*getInstance()函数该怎么理解?
static ShoppingCartManager* getInstance()
{
static ShoppingCartManager Instance; // 静态局部变量,只会初始化一次
return &Instance; // 返回实例的指针
}
static ShoppingCartManager* getInstance()
是一个典型的 单例模式(Singleton Pattern) 的实现。它的作用是确保整个程序中只有一个 ShoppingCartManager
的实例,并提供全局访问点来获取这个实例。
单例模式的核心思想
单例模式是一种设计模式,用于确保一个类只有一个实例,并提供一个全局访问点来访问这个实例。这在某些场景下非常有用,比如管理全局状态(如购物车、配置管理器等)。
1. static
关键字的作用
(1)在函数内部:static ShoppingCartManager Instance;
表示 Instance
是一个静态局部变量。静态局部变量的生命周期是整个程序运行期间,且只会被初始化一次。
(2)在函数返回值类型前:static ShoppingCartManager*
表示这个函数是一个静态成员函数。静态成员函数属于类本身,而不是类的某个对象,因此可以直接通过类名调用(如 ShoppingCartManager::getInstance()
)。
2. 为什么使用静态局部变量?
(1)唯一性:静态局部变量只会被初始化一次,因此无论 getInstance()
被调用多少次,Instance
只会被创建一次。
(2)延迟初始化:Instance
只有在第一次调用 getInstance()
时才会被创建,而不是在程序启动时就创建。这称为 懒汉模式(Lazy Initialization)。
3. 为什么返回指针?
(1)返回指针是为了避免对象被复制。如果返回对象本身,可能会导致意外的对象拷贝,破坏单例的唯一性。
(2)返回指针也方便在需要时传递实例的地址。
4. 如何使用 getInstance()
在 main
函数中,我们可以这样使用:
ShoppingCartManager* cart = ShoppingCartManager::getInstance();
-
这里
cart
是一个指向ShoppingCartManager
实例的指针,无论你在程序的哪个地方调用getInstance()
,它都会返回同一个实例。
5. 单例模式的优点
(1)全局唯一:确保整个程序中只有一个实例。
(2)全局访问:可以通过 getInstance()
在任何地方访问这个实例。
(3)延迟初始化:实例只有在第一次使用时才会被创建,节省资源。
6. 单例模式的缺点
(1)全局状态:单例模式引入了全局状态,可能会导致代码难以测试和维护。
(2)线程安全问题:在多线程环境下,如果没有正确处理,可能会导致多个线程同时创建实例。
示例代码的运行流程
-
第一次调用
(1)静态局部变量getInstance()
时:Instance
被创建。(2)返回Instance
的地址。 -
后续调用
直接返回之前创建的getInstance()
时:Instance
的地址。
总结
static ShoppingCartManager* getInstance()
的作用是:
(1)确保 ShoppingCartManager
只有一个实例。
(2)提供一个全局访问点来获取这个实例。
(3)使用静态局部变量实现延迟初始化和线程安全。