单例模式
本课属于 Coddy C++ 学习旅程的“面向对象编程”单元。第 91 课,共 104 课。
单例模式确保一个类在整个程序中只有一个实例,并提供全局访问该实例的入口。这适用于需要共享的资源,例如配置管理器、日志记录器或数据库连接池。
核心思路是将构造函数设为私有,这样就没有人能直接创建实例,然后提供一个静态方法来返回唯一的实例:
#include <iostream>
#include <string>
class Logger {
private:
std::string logFile;
// 私有构造函数,防止直接创建实例
Logger() : logFile("app.log") {}
// 删除拷贝构造函数和赋值运算符
Logger(const Logger&) = delete;
Logger& operator=(const Logger&) = delete;
public:
// 用于访问唯一实例的静态方法
static Logger& getInstance() {
static Logger instance; // 只创建一次,直到程序结束才销毁
return instance;
}
void log(const std::string& message) {
std::cout << "[LOG] " << message << "\n";
}
};
int main() {
Logger::getInstance().log("Application started");
Logger& logger = Logger::getInstance();
logger.log("Processing data");
}static Logger instance 位于 getInstance() 内部,这是 C++11 的一项特性,称为“魔法静态变量”,它是线程安全的,并且保证只初始化一次。通过删除复制构造函数和赋值运算符,我们可以防止任何人创建单例的副本。
谨慎使用单例模式,因为它会引入全局状态,可能让测试变得更困难。它最适合用于真正需要共享的资源,因为创建多个实例可能会导致问题。
挑战
简单让我们构建一个 GameSettings 单例,用于管理游戏应用的配置。这是单例模式的理想用例。你需要一个唯一的设置管理器,让游戏的所有部分都能访问它,从而确保整个游戏的配置一致。
你将把代码组织在三个文件中:
GameSettings.h:定义用于管理游戏配置的单例类。你的
GameSettings类应将三个设置存储为私有成员:difficulty(字符串)、volume(0-100 的整数)和fullscreen(布尔值)。将它们初始化为默认值:"Normal"、50和false。正确实现单例模式:
- 将构造函数设为私有
- 删除复制构造函数和赋值运算符
- 提供一个静态
getInstance()方法,返回唯一实例的引用
添加以下公共方法来操作这些设置:
setDifficulty(const std::string& diff):更新难度setVolume(int vol):更新音量setFullscreen(bool fs):更新全屏模式displaySettings():输出所有当前设置
GameModule.h:创建一个用于访问单例的独立模块。定义一个名为
applyGamePreset的函数,它接受一个预设名称(字符串)。此函数应访问GameSettings单例,并根据预设进行配置:"casual":将难度设为"Easy",音量设为70,全屏设为false"competitive":将难度设为"Hard",音量设为30,全屏设为true- 其他任何预设:保持默认设置不变
这展示了程序的不同部分如何访问并修改同一个单例实例。
main.cpp:将所有内容整合起来,展示单例的实际用法。读取两个输入:
- 一个预设名称(字符串)
- 一个自定义音量等级(整数)
首先,在单例上调用
displaySettings(),输出初始默认设置。之后输出一个空行。然后,使用你的
applyGamePreset函数应用预设,并再次输出设置。之后输出一个空行。最后,仅使用自定义输入值覆盖音量,并输出最终设置。
displaySettings() 方法应按以下确切格式输出:
Difficulty: [value]
Volume: [value]
Fullscreen: [yes/no]例如,输入 casual 和 85 时:
Difficulty: Normal
Volume: 50
Fullscreen: no
Difficulty: Easy
Volume: 70
Fullscreen: no
Difficulty: Easy
Volume: 85
Fullscreen: no输入 competitive 和 20 时:
Difficulty: Normal
Volume: 50
Fullscreen: no
Difficulty: Hard
Volume: 30
Fullscreen: yes
Difficulty: Hard
Volume: 20
Fullscreen: yes注意,main.cpp 和 GameModule.h 都访问同一个单例实例:在一处所做的更改在其他地方也能看到。这就是单例模式用于游戏设置等共享资源的优势。
亲手试试
#include <iostream>
#include <string>
#include "GameSettings.h"
#include "GameModule.h"
using namespace std;
int main() {
// 读取输入
string presetName;
int customVolume;
cin >> presetName;
cin >> customVolume;
// TODO: 获取 GameSettings 单例实例
// TODO: 显示初始默认设置
// TODO: 输出一个空行
// TODO: 使用 applyGamePreset 函数应用预设
// TODO: 显示应用预设后的设置
// TODO: 输出一个空行
// TODO: 用 customVolume 覆盖音量设置
// TODO: 显示最终设置
return 0;
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: C++ 在线编译器