单例模式设计解析
单例模式是一种确保类仅有一个实例的设计模式,常用于需要全局统一访问点的场景。例如,日志记录器通常设计为单例,以便所有模块共享同一个日志实例;或者主控制器模块为了协调各子模块操作,采用单例来保证数据一致性。
C语言实现单例模式
在C语言中,可以通过结构体和静态变量模拟面向对象的单例模式。
#include <stdio.h>
#include <stdlib.h>
typedef struct {
void* buffer;
int count;
} Instance;
static Instance* instance = NULL;
Instance* getInstance() {
if (!instance) {
instance = (Instance*)malloc(sizeof(Instance));
if (instance) {
instance->buffer = NULL;
instance->count = 0;
}
}
return instance;
}
void destroyInstance() {
if (instance) {
free(instance);
instance = NULL;
}
}
int main() {
Instance* a = getInstance();
a->count = 42;
Instance* b = getInstance();
printf("Value: %d\n", b->count); // 输出 42
destroyInstance();
return 0;
}
上述代码中,getInstance()使用静态局部变量实现懒汉式单例:首次调用时分配内存并初始化,后续调用直接返回已有实例。注意需手动管理内存释放。
C++经典单例模式(Meyer's Singleton)
C++中常用Scott Meyers提出的懒汉式单例,利用函数局部静态变量实现线程安全(C++11起保证初始化线程安全)。
#include <iostream>
class Logger {
public:
static Logger& get() {
static Logger instance; // 局部静态变量,C++11起线程安全
return instance;
}
void log(const std::string& msg) {
std::cout << "[LOG]: " << msg << std::endl;
}
private:
Logger() = default;
~Logger() = default;
Logger(const Logger&) = delete;
Logger& operator=(const Logger&) = delete;
};
int main() {
Logger::get().log("System started");
Logger& logger = Logger::get();
logger.log("Another message");
return 0;
}
通过将构造函数私有化,禁止拷贝构造和赋值,确保只能通过get()获取唯一实例。函数内部的静态变量在首次调用时创建,程序结束时自动销毁。
C++饿汉式单例
饿汉式在类加载时直接初始化静态成员实例,适用于实例初始化代价低或必须提前准备的场景。
#include <iostream>
class Config {
private:
static Config instance; // 类加载即创建
int version;
Config() : version(1) {}
public:
static Config& get() {
return instance;
}
int getVersion() const { return version; }
void setVersion(int v) { version = v; }
Config(const Config&) = delete;
Config& operator=(const Config&) = delete;
};
Config Config::instance; // 静态成员定义
int main() {
Config& conf = Config::get();
std::cout << "Version: " << conf.getVersion() << std::endl;
conf.setVersion(2);
Config& conf2 = Config::get();
std::cout << "Version: " << conf2.getVersion() << std::endl; // 输出2
return 0;
}
饿汉式的实例在程序启动时即完成构造,天然线程安全,但可能造成启动延迟或资源浪费(如果实例从未被使用)。
线程安全问题
饿汉式由于在类加载期间完成初始化,多线程访问时不存在竞争条件,是线程安全的。懒汉式(非局部静态变量版本)在首次创建实例时可能被多个线程同时执行,导致创建多个对象。解决方案包括:
- 使用C++11的函数局部静态变量(如Meyer's Singleton),编译器保证初始化原子性。
- 使用互斥锁保护创建过程,如
std::lock_guard搭配双重检查锁定。 - 使用
std::call_once和std::once_flag确保创建代码只执行一次。
现代C++推荐优先使用函数局部静态变量方案,因标准库已经保证了线程安全性。