直接来看代码:
1 #include <iostream> 2 3 class Singleton 4 { 5 private: 6 Singleton(){std::cout<<"Singleton()"<<std::endl;}; 7 Singleton(const Singleton&) = delete; 8 Singleton& operator=(const Singleton&)= delete; 9 static Singleton* _instance; 10 11 public: 12 static Singleton* Instance() 13 { 14 if(_instance == nullptr) 15 { 16 _instance = new Singleton(); 17 } 18 return _instance; 19 } 20 21 void print() 22 { 23 std::cout<<"print()"<<std::endl; 24 } 25 26 void destory() 27 { 28 delete _instance; 29 _instance = nullptr; 30 } 31 32 ~Singleton() 33 { 34 std::cout<<"~Singleton()"<<std::endl; 35 } 36 }; 37 Singleton* Singleton::_instance = nullptr; 38 39 int main() 40 { 41 Singleton::Instance()->print(); 42 Singleton::Instance()->destory(); 43 }
一般的单例模式Demo是没有 destory() 方法的,就导致一个问题,在程序结束之后,该单例对象没有被delete,导致内存泄露,可以通过 valgrind 来查看。
优点:采用懒汉模式;确实只能产生一个对象,可以防止 Singleton obj = *Singleton::Instance(); 这样的代码产生额外对象;由于 new 操作是线程安全的,所以在多线程环境中也可以保证只产生一个对象;在程序退出前调用 destory() 方法,可避免内存泄露,另外由于 destory() 方法必须由 Singleton::Instance() 调用,所以当该方法被调用时,可以保证 new 操作已经完成。
缺点:必须在程序结束前调用 Singleton::Instance()->destory(); 方法,显得很麻烦;
另外,也有人认为上面并不是线程安全的,需要加锁。但我不这么认为,如果你确实不放心的话,可以使用饿汉模式避免这个话题:
1 class Singleton 2 { 3 private: 4 Singleton(){std::cout<<"Singleton()"<<std::endl;}; 5 Singleton(const Singleton&) = delete; 6 Singleton& operator=(const Singleton&)= delete; 7 static Singleton* _instance; 8 9 public: 10 static Singleton* Instance() 11 { 12 return _instance; 13 } 14 15 void destory() 16 { 17 delete _instance; 18 _instance = nullptr; 19 } 20 21 ~Singleton() 22 { 23 std::cout<<"~Singleton()"<<std::endl; 24 } 25 }; 26 Singleton* Singleton::_instance = new Singleton();
静态初始化实例可以保证线程安全,因为静态实例初始化在程序开始时进入主函数之前,就由主线程以单线程方式完成了初始化。
缺点:但静态初始化的顺序是不一定的,在很多时候要注意这一点,与单纯的此议题无关,除非……两个类都使用了单例模式,且一个类在构造函数中调用了另一个类的对象,而它可能还没有被构造。
那如何解决上面的那个必须使用 destory() 方法来手动在程序结束前清理堆内存的问题呢?
1 class Singleton 2 { 3 private: 4 Singleton(){std::cout<<"Singleton()"<<std::endl;}; 5 Singleton(const Singleton&) = delete; 6 Singleton& operator=(const Singleton&)= delete; 7 8 public: 9 static Singleton& Instance() 10 { 11 static Singleton _instance; 12 return _instance; 13 } 14 15 ~Singleton() 16 { 17 std::cout<<"~Singleton()"<<std::endl; 18 } 19 };
在一个函数中使用静态变量,该静态变量在函数第一次被调用时初始化,如果另一个线程在它初始化完成之前试图调用该函数,它必须等待(仅从 c++0x 开始),所以这里是线程安全的。另外当程序结束时,也不会有内存泄露问题。
同样的,如果质疑线程安全问题,或者未使用 c++0x 编译器,也可以使用饿汉模式,不过同样的:两个类都使用了单例模式,且一个类在构造函数中调用了另一个类的对象,而它可能还没有被构造。所以,最终还是推荐上面的写法,使用 c++0x 编译器,可以解决前面提到的全部问题。