目的
本例简介C++中单例模式的最优写法。
实现
基础写法
下面的代码是C++单例的基础写法,在静态函数Singleton::getInstance()中定义了Singleton的静态变量对象,并返回此对象的引用。
由于C++函数的静态变量唯一性,可以确保例子中s对象的唯一性,线程同步,以及静态对象间的依赖关系等问题。
#include <iostream>
class Singleton {
public: static Singleton &getInstance() {
static Singleton s;
return s;
}
public: void test() {
std::cout << "test" << std::endl;
}
};
int main() {
Singleton &s = Singleton::getInstance();
s.test();
return 0;
}
完整写法
如果对代码要求比较严格,可以把该关闭的函数都关掉,这取决与你:):
- 构造函数私有化,使得外部无法创建Singleton对象。
- 关闭拷贝构造函数,右值拷贝构造函数。
- 关闭赋值运算符重载函数。
class Singleton {
private: Singleton() { }
Singleton(const Singleton &) = delete;
Singleton(const Singleton &&) = delete;
Singleton &operator=(const Singleton &) = delete;
public: static Singleton &getInstance() {
static Singleton s;
return s;
}
public: void test() {
std::cout << "test" << std::endl;
}
};
对比
New
使用下面方法的也比较多,缺点是在无法保证getInstance()的线程安全性。如果工程比较大,会存在多个线程同时调用Singleton::getInstance()方法导致创建多实例的问题。
class Singleton {
private: static Singleton *instance;
public: static Singleton *getInstance() {
if (NULL == instance)
instance = new Singleton();
return instance;
}
};
Lock
所以可能会加一堆lock, 为了性能还写个double check, 比如下面这样,写起来比较麻烦,个人不太喜欢。
class Singleton {
private: static Singleton *instance;
public: static Singleton *getInstance() {
if (NULL == instance) {
// TODO LOCK
if (NULL == instance)
instance = new Singleton();
}
return instance;
}
};
静态成员
如果把函数内的静态变量变成类的静态成员变量呢?简单的工程行,复杂的不行。因为如果静态类之间有依赖,可能会导致C++的一些未定义的行为。
- 下例中的Singleton::instance 保存在程序全局的静态数据区,instance初始化的时机是在程序的main()函数执行前。
- 假设有SingletonB::instance,与Singleton::instance类似定义,也是静态类成员变量。SingletonB::instance和Singleton::instance的初始化顺序是未定义的,得看编译器的心情。
- 如果Singleton::instance 的初始化在SingletonB::instance之前,而Singleton的构造函数中恰好需要引用到SIngleonB::instance,就很可能会出现一些未定义的行为。
#include <iostream>
class Singleton {
private: static Singleton instance;
public: static Singleton &getInstance() {
return instance;
}
public: void test() {
std::cout << "test" << std::endl;
}
};
Singleton Singleton::instance;
int main() {
Singleton &s = Singleton::getInstance();
s.test();
return 0;
}
总结
- 如果项目小,建议上述单例中的简单写法就够了。
- 如果项目大,可以写全点儿。
引用
[1] Efficitive C++