标签:单例模式 c++
大致思路是,将该类的构造函数定义为私有方法,代码其他地方不能实例化该对象,只能通过调用该类的一个静态成员函数(get_instance())来获取这个唯一实例。
更进一步,把该类的复制构造函数和重载的=赋值运算也声明为私有,即Singleton(const Singleton)和 Singleton & operate = (const Singleton&)函数,需要声明成私有的,并且只声明不实现.
这里有个小坑是,当这个唯一的实例没有被创建时,有多个线程同时调用get_instance(),可能会造成过个实例的创建。因此需要牺牲一点效率做处理,如:加锁和双检测(double-check)以保护。
对于实例的创建,又有饿汉和懒汉两种模式,懒汉即在使用对象时才进行创建,如上面提到的,存在线程安全性问题。有一点补充的是,C++0X以后,要求编译器保证内部静态变量的线程安全性,可以不加锁。但C++ 0X以前,仍需要加锁。
而饿汉模式是,在进入主函数之前就由主线程以单线程方式完成了初始化,不必担心多线程问题,在性能需求较高时,应使用这种模式,避免频繁的锁争夺。但是多个单例模式的类相互引用时,由于静态成员变量 初始化顺序没有保障,会有坑出现,详见:
http://blog.csdn.net/crayondeng/article/details/24853471
以一个日志类为例:
考虑到日志文件只有一个的特点,我们用一个单例模式来实现,在其构造函数中完成对日志文件的初始化(打开和基本设置),其后每次读写操作只需拿到有效的文件句柄。
类的声明如下:
class Logger{
public:
static Logger* get_instance();
int log_write(String &str, int errcode);
int log_read(String &str, int errcode);
static int mutex_init();
static pthread_mutex_t *mutex_for_creating;
private:
Logger();
~Logger();
Logger(const Logger &);
Logger & operator = (const Logger &);
static Logger* p_logger;
int fd;
};
懒汉模式获取实例的函数大致如下:
static Logger* Logger::get_instance()
{
if (p_logger)
return p_logger;
pthread_mutex_lock(&mutex_for_creating);
if (!p_logger) {
p_logger = new Logger;
}
pthread_mutex_unlock(&mutex_for_creating);
return p_logger;
}
两个if语句即上面提到的双检测机制,确保线程安全。
进入程序前有两个初始化:
static Logger::p_logger = NULL;
static int Logger::mutex_init()
{
if ((mutex_for_creating = new pthread_mutex_t) == NULL)
return -1;
pthread_mutex_init(&mutex_for_creating, NULL);
return 0;
}
而对于饿汉模式:
将实例的指针p_logger置为public,直接在进入主程序前new一个对象:
static Logger::p_logger = new Logger;
关于对象生命周期,一般可以不考虑,因为该对象通常是存在在整个程序的生命周期,随着程序的退出自动销毁。
而有些情况,如:“在类中,有一些文件锁了,文件句柄,数据库连接等等,这些随着程序的关闭而不会立即关闭的资源,必须要在程序关闭前,进行手动释放”[1]
以及http://www.programlife.net/cpp-singleton-memory-retrieve.html
静态局部变量具有局部作用域,它只被初始化一次,自从第一次被初始化直到程序运行结束都一直存在,它和全局变量的区别在于全局变量对所有的函数都是可见的,而静态局部变量只对定义自己的函数体始终可见。
简单地说static 局部变量相对于局部变量:改变局部变量的存储位置,不改变局部变量的作用范围。
参考:[1] http://www.jellythink.com/archives/82
单例模式初探
标签:单例模式 c++
原文地址:http://blog.csdn.net/simon_xia_uestc/article/details/39639459