单例模式写法
单例模式,Singleton Pattern,就是保证在程序的整个生命周期中只创建一个类的实例(对象),在设计日志的时候尤为常见。
1 C++中的单例模式
在C++中实现单例模式有很多种方式,这里采用最常见的实现方式之一:基于静态局部变量,Meyers' Singleton。这种方式利用了C++11及以后标准中对静态局部变量初始化的保证,实现了线程安全、懒加载。
class Example {
private:
Example() = default;
// 禁用拷贝构造函数和赋值运算符
Example(const Example&) = delete;
Example& operator=(const Example&) = delete;
public:
static Example& getInstance() {
static Example instance;
return instance;
}
};
接着来测试一下
int main() {
Example& a = Example::getInstance();
Example& b = Example::getInstance();
if (&a == &b) cout << "a and b are the same instance.\n";
return 0;
}
- 懒加载,Lazy Initialization。只有在第一次调用
getInstance时才创建实例,而不是程序启动的时候。 - 线程安全:初始化过程保证了互斥和同步,避免多线程并发。
static关键字
- static关键字修饰局部变量
函数
getInstance中的static关键字修饰了instance这个局部变量,指定该变量只能初始化一次,并在之后再次调用该函数的时候保留其状态。这是因为static关键字改变了局部变量的生命周期(Storage Duration),它本应该在函数调用完成之后销毁的,但实际上被保留在了C++内存分布中的静态区,直到程序结束后才释放。尽管如此,其作用域(Scope)仍然局限于其所在的函数内部
- static修饰成员函数——静态成员函数。在讲解静态成员函数之前,先介绍静态成员变量。
静态成员变量,就是被static修饰的类成员变量,有以下几个规则:
- 必须在类中声明,类外初始化;
- 被所有类的对象(实例)共享,不属于某一个对象;
- 也具有public、protected、private三种访问级别,访问的方式和普通的类似;
- 可以通过三种方式(对象、类、匿名对象)进行访问:
class A { public: static int s_num; // 默认为0 }; int A::s_num = 2; int main() { A a; cout << a.s_num << endl; // 对象 cout << A::s_num << endl; // 类 cout << A().s_num << endl; // 匿名对象 突破类域 }
由于静态成员函数没有this指针,不能访问非静态成员变量。因为this指针是属于一个实例的,每一个被创建的实例都拥有其独特的this指针,维护其本身所具有的成员变量。
2 Python实现单例模式
在Python中使用构造方法__new__来控制实例的创建过程,实现单例模式只需要在类中存储和表示一个类的实例__instance,并且加一个判断:类是否有对象。
__new__属于魔术方法,不需要手动调用,除了cls接收当前类,其它的参数需要和__init__保持一致。它返回object.__new__(cls)来构造实例。
class Demo:
__instance = None
def __new__(cls, *args, **kwargs):
if not cls.__instance:
cls.__instance = object.__new__(cls) # or super().__new__(cls)
return cls.__instance
a = Demo()
b = Demo()
print(a is b)
需要注意的是,__init__可能会被多次调用,从而把原有的成员变量的值给覆盖了,这是和C++不同的地方。上述实现并没有考虑多线程并发的场景,因此在并发场景下需要改进:
from threading import Lock
class Demo:
__instance = None
__lock = Lock()
def __new__(cls):
if not cls.__instance:
with cls.__lock:
cls.__instance = super().__new__(cls)
return cls.__instance
上述代码并没有测试在并发场景下是否是正确的,未来看看对不对

浙公网安备 33010602011771号