单例模式
单例模式是一种创建型设计模式,核心目标是:保证一个类在程序生命周期内只有一个实例,并提供一个全局访问点。
常见的使用场景:配置管理器、日志管理器、数据库连接池等(这类对象只需要一个实例,多实例会导致资源浪费或逻辑冲突)。
实现要点
-
私有化构造函数 / 拷贝构造 / 赋值运算符,防止外部创建
-
提供静态方法获取唯一实例
-
线程安全(C++11 后静态局部变量天生线程安全)
-
函数内的静态局部变量初始化时,若多个线程同时进入初始化语句,只有一个线程完成初始化,其他线程阻塞等待初始化完成。
因此 static Singleton UniqueInstance; 是天然线程安全的,无需手动加锁(如 std::mutex)
-
#include <iostream>
#include <mutex>
class Singleton {
private:
// 私有化构造函数,禁止外部创建
Singleton() {
std::cout << "Singleton 实例创建" << std::endl;
}
// 传统懒汉式(new出的单例,因为 new 强制要求生命周期由程序员管理
// 要手动delete)退出时不会调用虚构函数,需要手动释放资源,否则会造成内存泄漏
// ~Singleton() {
// std::cout << "Release Resources!" << std::endl;
// };
// 禁用拷贝和赋值
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
Singleton(Singleton&&) = delete;
Singleton& operator=(Singleton&&) = delete;
public:
// 全局访问点
static Singleton& getInstance() {
// C++11起,静态局部变量初始化是线程安全的
static Singleton instance;
return instance;
}
// 示例方法
void doSomething() {
std::cout << "单例对象执行操作" << std::endl;
}
};
// 测试
int main() {
// 获取唯一实例
Singleton& s1 = Singleton::getInstance();
Singleton& s2 = Singleton::getInstance();
// 验证是同一个实例
std::cout << "&s1: " << &s1 << std::endl;
std::cout << "&s2: " << &s2 << std::endl;
s1.doSomething();
return 0;
}
场景题
场景1:配置管理器的单例实现
需求描述
开发一个应用配置管理器(ConfigManager),要求:
实现代码
#include <iostream>
#include <string>
#include <unordered_map>
#include <fstream>
#include <nlohmann/json.hpp> // 需安装nlohmann/json库:https://github.com/nlohmann/json
// 配置管理器单例类
class ConfigManager {
private:
// 私有化构造函数,在构造时加载配置文件
ConfigManager(const std::string& configPath) {
loadConfig(configPath);
std::cout << "配置管理器实例创建,加载配置文件:" << configPath << std::endl;
}
// 禁用拷贝、移动语义
ConfigManager(const ConfigManager&) = delete;
ConfigManager& operator=(const ConfigManager&) = delete;
ConfigManager(ConfigManager&&) = delete;
ConfigManager& operator=(ConfigManager&&) = delete;
// 存储配置项的容器
std::unordered_map<std::string, std::string> configMap;
// 加载配置文件的私有方法
void loadConfig(const std::string& configPath) {
std::ifstream file(configPath);
if (!file.is_open()) {
throw std::runtime_error("配置文件打开失败:" + configPath);
}
nlohmann::json jsonConfig;
file >> jsonConfig;
// 将JSON配置转换为键值对存储
for (auto& [key, value] : jsonConfig.items()) {
configMap[key] = value.get<std::string>();
}
}
public:
// 全局访问点:懒加载+线程安全
static ConfigManager& getInstance(const std::string& configPath = "config.json") {
static ConfigManager instance(configPath);
return instance;
}
// 获取配置项(只读)
std::string getConfig(const std::string& key) const {
auto it = configMap.find(key);
if (it == configMap.end()) {
throw std::invalid_argument("配置项不存在:" + key);
}
return it->second;
}
};
// 测试代码
int main() {
try {
// 获取配置管理器实例并读取配置
ConfigManager& config = ConfigManager::getInstance("config.json");
std::cout << "数据库地址:" << config.getConfig("db_host") << std::endl;
std::cout << "数据库端口:" << config.getConfig("db_port") << std::endl;
std::cout << "日志级别:" << config.getConfig("log_level") << std::endl;
// 验证单例唯一性
ConfigManager& config2 = ConfigManager::getInstance();
std::cout << "\\n实例地址验证:" << std::endl;
std::cout << "&config: " << &config << std::endl;
std::cout << "&config2: " << &config2 << std::endl;
} catch (const std::exception& e) {
std::cerr << "错误:" << e.what() << std::endl;
return 1;
}
return 0;
}
配置文件示例(config.json)
{
"db_host": "127.0.0.1",
"db_port": "3306",
"log_level": "INFO",
"max_connection": "100"
}
实现说明
场景2:单例模式的常见面试题
问题1:饿汉式 vs 懒汉式单例,各自的优缺点?
| 饿汉式 | 程序启动时(静态成员初始化阶段)创建实例 | 1. 天生线程安全;2. 实现简单 | 1. 提前占用内存;2. 若实例依赖其他未初始化的资源,可能导致初始化失败 |
| 懒汉式 | 第一次调用getInstance时创建实例 | 1. 懒加载,节约内存;2. 实例化时机可控 | 1. C++11前需手动加锁保证线程安全;2. 可能存在竞态条件(C++11已解决) |
饿汉式实现示例:
class HungrySingleton {
private:
// 程序启动时初始化,全局唯一
static HungrySingleton instance;
HungrySingleton() = default;
HungrySingleton(const HungrySingleton&) = delete;
HungrySingleton& operator=(const HungrySingleton&) = delete;
public:
static HungrySingleton& getInstance() {
return instance;
}
};
// 类外初始化静态成员
HungrySingleton HungrySingleton::instance;
问题2:如何解决单例模式的“内存泄漏”问题?
传统懒汉式若用new创建实例(如下),实例不会自动析构,易导致内存泄漏:
// 有内存泄漏风险的懒汉式
static Singleton* getInstance() {
static Singleton* instance = new Singleton(); // 不会自动delete
return instance;
}
解决方案:



