• C++实现Creational Singleton模式


    http://patmusing.blog.163.com/blog/static/135834960201002322226231/

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

    2010-01-23 02:22:26|  分类: Pattern |  标签:c++设计模式  |字号 订阅

     
     

    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

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

  • 相关阅读:
    mapr
    短信 流控规则
    js modify local file
    An O(ND) Difference Algorithm and Its Variations (1986)
    美团金融扫码付静态资源加载优化实践
    前端遇上Go: 静态资源增量更新的新实践
    小程序短信验证码登录的实现与优化
    A Practical Introduction to Blockchain with Python
    numpy计算
    小程序登录方式切换 不做url跳转
  • 原文地址:https://www.cnblogs.com/vimmer/p/2998808.html
Copyright © 2020-2023  润新知