一 实现懒汉式单例模式的关键
通过查询懒汉式单例模式的实现方法,发现以前自己忽略的一个知识细节,就是静态(static)成员函数的用法。
- this指针的产生
构造函数构建实例对象分为两个阶段:①分配内存:这是从无到有阶段,该阶段结束,产生一个实例对象(instance),指向这个实例对象内存的指针,称为this;②初始化内存:真正调用构造函数的部分。此时已经是对该instance调用构造函数,因此,可以调用任何非静态函数。 - 非静态成员函数调用类的成员函数
由于非静态成员函数在使用时,已经产生了实例对象,也就是这个this指针,因此,编译器在生成汇编代码时,将这个this指针和成员函数的各个参数全部压栈,就实现了用各个对象的数据调用成员函数的目的。 - 静态成员函数可调用构造函数
静态成员函数拥有类的作用域,但是没有隐藏的this指针,因此一般static成员函数不能访问non-static成员变量和non-static成员函数。不过有个例外,static成员函数可以调用构造函数。由于构造函数特殊性,它从无到有构造一个对象,因此调用它不需要一个实例(instance),也就是不需要this指针来调用,所以在static 函数中能调用构造函数。
二 懒汉式单例模式的实现方式
实现方式1:
//该方式存在内存泄漏
#include <iostream>
class Singleton {
private:
Singleton() {
std::cout << "constructor called!" << std::endl;
}
Singleton(Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static Singleton* m_instance_ptr;
public:
~Singleton() {
std::cout << "destructor called!" << std::endl;
}
static Singleton* get_instance() {
if (m_instance_ptr == nullptr) {
m_instance_ptr = new Singleton; //请注意,在静态成员函数中调用了非静态成员变量,这是特例,仅对构造函数有用
}
return m_instance_ptr;
}
void use() const { std::cout << "in use" << std::endl; }
};
Singleton* Singleton::m_instance_ptr = nullptr;
int main()
{
Singleton* instance = Singleton::get_instance();
Singleton* instance_2 = Singleton::get_instance();
return 0;
}
在上述例程中可以看到,Singleton::get_instance函数直接调用了Singleton的默认构造函数,类的构造函数不也是类的非静态成员函数吗?为什么get_instance能访问呢?这是因为在调用类的构造函数时,new Singleton不需要提供一个this指针,因此类的静态成员函数可以调用构造函数;至于this指针,是在new完以后才产生的。
上述例程存在一个问题,Singleton::get_instance()是在堆上分配了内存,但是在程序结束时没有释放该内存,因此存在内存泄漏问题。
最好的方式如下:
//这种方法又叫做 Meyers' SingletonMeyer's的单例,最推荐的方式
#include <iostream>
class Singleton
{
public:
~Singleton() {
std::cout << "destructor called!" << std::endl;
}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static Singleton& get_instance() {
static Singleton instance; //请注意,在静态成员函数中调用了非静态成员变量,这是特例,仅对构造函数有用
return instance;
}
private:
Singleton() {
std::cout << "constructor called!" << std::endl;
}
};
int main(int argc, char *argv[])
{
Singleton& instance_1 = Singleton::get_instance();
Singleton& instance_2 = Singleton::get_instance();
return 0;
}
请注意,在上述两种懒汉模式中,均在静态成员函数中调用了构造函数,这是单例模式的关键。