1. C++实现Creational - Singleton模式

转载 2012年03月28日 22:51:43

1. C++实现Creational - Singleton模式  


Singleton设计模式经常被大家谈及,很多人认为该模式很简单。的确,从纯粹的设计模式的角度来看,它并不复杂,但是从实现的角度来看,其实非常不简单,尤其是用C++去实现它的时候。

 

一、Java版本的Singleton模式实现

我们不妨先看看在Java中实现Singleton模式的典型代码:

// Singleton设计模式典型代码

package Singleton;

public class Singleton

{

         private Singleton(){}

 

         private static Singleton instance = null;

         public static synchronized Singleton getInstance()

         {

                   if(instance == null)

                   {

                            instance = new Singleton();

                   }

                   return instance;

         }

}

 

// 测试Singleton

package Singleton;

public class PatternClient

{

         public static void main(String args[])

         {

                   Singleton sg = Singleton.getInstance();

         }

}

 

从上面的代码,可以看出,在Singleton类中:

1.       构造方法是private的;

2.       有一个private的静态变量,其类型就是Singleton本身;

3.       有一个public的方法getInstance(),其返回类型是Singleton

4.       必须注意getInstance()有关键字synchronized修饰,以提供线程安全。

 

1. C++实现Singleton模式 - 玄机逸士 - 玄机逸士博客 

 

下面我们来看看如何在C++中实现Singleton设计模式。

 

二、最简单的实现方式

先看下面的代码,并注意相关的注释:

// Singleton.h

// C++:最简单的方式实现Singleton设计模式

#include <string>

#include <iostream>

using namespace std;

class Singleton

{

private:

         Singleton()                                            // private构造函数

         {

                   cout << "Singleton::Constructor" << endl;

         }

 

         static Singleton* sg;                               // 私有静态变量,其类型为Singleton*

                                                                     // 形如Singleton sg;这样的声明会出编译时错误

public:

         static Singleton* getInstance()                 // public成员函数getInstance()

         {

                   cout << "Singleton::getInstance" << endl;

                   if(!sg)

                   {

                            sg = new Singleton();           // (1)在这个地方new了一个对象,但是在什么地方delete呢?

                   }

 

                   return sg;

         }

};

 

// 测试Singleton的代码:Singleton.cpp

#include "Singleton.h"

// 用下面的方式对静态私有成员变量sg进行初始化,此时sg不指向任何对象

Singleton* Singleton::sg = 0;

int main(void)

{

         Singleton *sg = Singleton::getInstance();

 

         return 0;

}

 

关于如何在一个类的定义中,使用类本身作为类型对成员变量进行声明的详细情况,请见:

http://blog.csdn.net/pathuang68/archive/2009/11/24/4866945.aspx

 

上面的Singleton的实现至少存在一个问题:

语句(1)new了一个对象,但没有被delete,因此肯定会造成内存泄漏。而在Java语言中由于有内存回收机制,所以不存在这个问题。不过既然是Singleton,通常都是和应用程序的生命周期是基本一致的,因此,实践中可以不考虑这个“内存泄露”问题,因为它根本没有机会造成真正意义上的泄露;另一方面,从纯技术的完备性角度来看,我们则需要解决这样的问题。

 

三、使用auto_ptr来解决内存泄漏问题

关于auto_ptr的原理和使用方法,请参考:

http://blog.csdn.net/pathuang68/archive/2009/11/29/4898101.aspx

http://blog.csdn.net/pathuang68/archive/2009/11/29/4900321.aspx

 

// Singleton.h

#include <memory>

#include <string>

#include <iostream>

using namespace std;

 

// C++:使用auto_ptr实现Singleton设计模式

class Singleton

{

private:

         Singleton()                                                                                 // private构造函数

         {

                   cout << "Singleton::Constructor" << endl;

         }

 

         static auto_ptr<Singleton> sg;                                                     // 私有静态变量,其类型为auto_ptr<Singleton>

 

public:

         static auto_ptr<Singleton> getInstance()                                       // public成员函数getInstance()

         {                                                                                                // 返回类型为auto_ptr<Singleton>

                   cout << "Singleton::getInstance" << endl;

                   if(!sg.get())                                                                        // 判断sg所指的对象是否为空

                   {

       // 此处不能直接写成auto_ptr<Singleton> sg(new Singleton);

                            auto_ptr<Singleton> temp(new Singleton);

                            sg = temp;                                             

                   }

 

                   return sg;

         }

};

 

// Singleton.cpp:测试Singleton的代码

#include "Singleton.h"

 

// 用下面的方式对静态私有成员变量sg进行初始化,此时sg不指向任何对象

auto_ptr<Singleton> Singleton::sg;

int main(void)

{

         // singleton就是我们需要的对象

         auto_ptr<Singleton> singleton(Singleton::getInstance());

 

         return 0;

}

 

这样以来,由于引入了auto_ptr,因此不会存在内存泄漏问题。进一步地,我们还可以将Singleton类,写成模板类,这样就可以更加灵活了。为此,我们另外增加一个类Student用来测试,Singleton模板类。

// Singleton.h

#include <memory>

#include <string>

#include <iostream>

using namespace std;

 

class Student

{

public:

         Student(const string name = "Andrew"const int age = 7) : name(name), age(age)

         {

                   cout << "constructor..." << endl;

         }

 

         void print_info() const

         {

                   cout << "Name: " << name << ", Age: " << age << endl;

         }

private:

         string name;

         int age;

};

 

// Singleton模板类

template<typename T>

class Singleton

{

private:

         Singleton()                                                                                           // private构造函数

         {

                   cout << "Singleton::Constructor" << endl;

         }

 

         static auto_ptr<T> sg;                                                                          // 私有静态变量,其类型为auto_ptr<T>

 

public:

         static auto_ptr<T> getInstance()                                                           // public成员函数getInstance()

         {                                                                                                         // 返回类型为auto_ptr<T>

                   cout << "Singleton::getInstance" << endl;

                   if(!sg.get())                                                                                // 判断sg所指的对象是否为空

                   {

                            // 此处不能直接写成auto_ptr<T> sg(new T);

                            auto_ptr<T> temp(new T);        

                            sg = temp;                                             

                   }

 

                   return sg;

         }

};

 

 

// Singleton.cpp 测试代码

#include "Singleton.h"

// 用下面的方式对静态私有成员变量sg进行初始化,此时sg不指向任何对象

auto_ptr<Student> Singleton<Student>::sg;

 

int main(void)

{

         auto_ptr<Student> singleton(Singleton<Student>::getInstance());

         singleton->print_info();

 

         return 0;

}

经过测试,上面的代码证明是可行的。

 

 

四、使用boost::mutex来解决线程安全性问题

Java代码中,有一个synchronized关键字,这个关键字的作用就是确保线程安全,很明显上面的代码尽管解决了内存泄漏的问题,但决线程安全方面的问题依然存在。

 

C++语言中,本身并不存在线程的概念,需要借助一些函数库才能实现,这种函数库有很多,其中一个比较出色的就是boost中的线程库了。boost库本身是可以跨操作系统平台的。

 

参考下面的代码和注释:

// Singleton.h

#include <memory>

#include <string>

#include <iostream>

#include <boost/thread/thread.hpp>

#include <boost/thread/mutex.hpp>

using namespace std;

 

boost::mutex sglt_mutex;

 

class Student

{

public:

         Student(const string name = "Andrew"const int age = 7) : name(name), age(age)

         {

                   cout << "constructor..." << endl;

         }

 

         void print_info() const

         {

                   cout << "Name: " << name << ", Age: " << age << endl;

         }

private:

         string name;

         int age;

};

 

 

template<typename T>

class Singleton

{

private:

         Singleton()                                                                                           // private构造函数

         {

                   cout << "Singleton::Constructor" << endl;

         }

 

         static auto_ptr<T> sg;                                                                           // 私有静态变量,其类型为auto_ptr<T>

 

public:

         static auto_ptr<T> getInstance()                                                             // public成员函数getInstance()

         {                                                                                                           // 返回类型为auto_ptr<T>

                   // 加锁,只有当前线程可以获取sg,其他线程均被阻塞(block)

                   // 直到当前线程执行sglt_mutex.unlock()

                   sglt_mutex.lock();                                                                         // (1)

                   cout << "Singleton::getInstance" << endl;

                   if(!sg.get())                                                                                  // 判断sg所指的对象是否为空

                   {

                            // 此处不能直接写成auto_ptr<T> sg(new T);

                            auto_ptr<T> temp(new T);        

                            sg = temp;                                             

                   }

 

                   return sg;

                  

         }

};

 

// Singleton.cpp

#include "Singleton.h"

 

// 用下面的方式对静态私有成员变量sg进行初始化,此时sg不指向任何对象

auto_ptr<Student> Singleton<Student>::sg;

 

void getSingletonInstanceAndDoSomeTediousWork()

{

         auto_ptr<Student> singleton(Singleton<Student>::getInstance());

 

         // 取得相关对象后,进行各种必要的运算:

         singleton->print_info();

         // .... do something else

 

         // 工作做完后,解锁。以便让别的线程获得机会。

         sglt_mutex.unlock();                                      // (2)

         // 如果没有上面这一句,则第一个获得锁的线程会无限时地阻止后续线程的运行

}

 

int main(void)

{

         boost::thread threadA(getSingletonInstanceAndDoSomeTediousWork);

         boost::thread threadB(getSingletonInstanceAndDoSomeTediousWork);

         boost::thread threadC(getSingletonInstanceAndDoSomeTediousWork);

         boost::thread threadD(getSingletonInstanceAndDoSomeTediousWork);

 

         threadA.join();

         threadB.join();

         threadC.join();

         threadD.join();

 

         return 0;

}

 

上面程序的结果如下:

Singleton::getInstance

constructor...

Name: Andrew, Age: 7

Singleton::getInstance

constructor...

Name: Andrew, Age: 7

Singleton::getInstance

constructor...

Name: Andrew, Age: 7

Singleton::getInstance

constructor...

Name: Andrew, Age: 7

 

上述结果符合预期。每次只能有一个线程,拥有对象,其他的则被阻塞,直到当前拥有锁的线程将锁解开。

 

如果函数getSingletonInstanceAndDoSomeTediousWork中的语句(2)被注释掉,那么输出结果将是:

Singleton::getInstance

constructor...

Name: Andrew, Age: 7

然后就是无限时的阻塞。

 

进一步地,如果将类Singelton定义中的(1)的这一句,即sglt_mutex.lock();也注释掉,那么输出的结果将是:

Singleton::getInstanceSingleton::getInstanceSingleton::getInstance

 

constructor...constructor...

constructor...Name: Name: Name: AndrewName: AndrewAndrew, Age: Andrew, Age: , Age: 7, Age: 77

这显然是没有同步了。因此,这不是我们所需要的。 


C++实现Creational - Singleton模式

Singleton设计模式经常被大家谈及,很多人认为该模式很简单。的确,从纯粹的设计模式的角度来看,它并不复杂,但是从实现的角度来看,其实非常不简单,尤其是用C++去实现它的时候。 一、Java版本的...

设计模式(Design Pattern) - 创建型模式(Creational Pattern) - 单例模式(Singleton Pattern) - Java实现

单例模式(Singleton Pattern):确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类成为单例类,提供全局访问的方法。实现单例模式的3个步骤: 1、private构...
  • achi010
  • achi010
  • 2017年06月25日 13:41
  • 155

C++实现Creational - Factory Method模式

设计模式的终极原理,本质上来说就是区分“变化的部分”和“稳定的部分”,并在此基础上,通过某种机制将此两者分离开来。此处所言之“变化”和“稳定”均为相对概念。在实现上,“稳定的部分”通常以抽象的形式出现...

C++实现Creational - Builder模式

在软件系统中,有时候面临着一个复杂对象的创建工作,该复杂对象通常由各个部分的子对象用一定的算法构成。由于需求的变化,这个复杂对象的各个子对象经常面临着剧烈的变化,但是将这些子对象组合在一起的算法是相对...

C++实现Creational - Prototype模式

抽象不能依赖于实现细节,实现细节应该依赖于抽象。   在软件系统中,经常面临着“某些结构复杂的对象”的创建工作,由于需求的变化,这些对象经常面临着剧烈的变化,但是他...

C++实现Creational - Simple Factory模式

Simple Factory模式根据提供给它的数据,返回几个可能类中的一个类的实例,通常这个类都有一个公共的父类和一些公共的方法。需要说明的是Simple Factory模式并非GoF所定义的23...

C++实现Creational - Abstract Factory模式

抽象工厂模式是所有形态的工厂模式中最为抽象和最具一般性的一种形态。   举例说明:Abstract Factory模式说的是抽象工厂,抽象工厂中继承出了两个实际工厂,这两个实际工厂开始生产对象,两...

Creational模式之Singleton模式

1.意图保证一个类仅有一个实例,并提供一个访问它的全局访问点。 深入了解请点击2.别名无3.动机对于一些类来说,只有一个实例是很重要的。虽然系统中可以有许多打印机,但却只应该有一个打印机工作,只应该...

C++完美实现Singleton模式

  • 2016年08月01日 10:40
  • 102KB
  • 下载

C++完美实现Singleton模式

  • 2014年05月21日 13:04
  • 16KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:1. C++实现Creational - Singleton模式
举报原因:
原因补充:

(最多只允许输入30个字)