C++中的单例模式

单例模式简介:
在设计或者开发中,有时会碰到一种情况:一个类只能有一个对象被创建,如果有多个对象的话,可能会导致混乱或者不一致。就比如在一个系统中,有一个管理配置信息的类,显然一个系统中只能有一份配置信息,所以这个类就可以用一个单例模式来实现。
类的定义如下:

class cfg_main_t
{
    public:
        static cfg_main_t* get_instance()
        {
            if(cfg_main == NULL)
                cfg_main = new cfg_main_t();
            return cfg_main;
        }
    private:
        cfg_main_t(){}
        static cfg_main_t cfg_main;
};

在cfg_main_t::get_instance()创建一个实例,但是只有在首次调用这个函数时才会创建此实例,未调用此函数时,此实例不会被创建。关于此类的定义有一些说明:
类的构造函数私有化了,程序创建一个对象时会首先调用其构造函数且一般由外部函数调用,若构造函数私有化,将导致编译出错。但是对于此类的本身可以利用static公用成员,因为类的static成员独立于类之外而无需产生对象对其进行访问。要产生一个单例对象必须调用构造函数,此时构造函数私有化了,私有数据只有类中的成员才能访问。所以类的static成员函数(get_instance())可以调用私有构造函数来创建一个实例,注意此函数的返回值是一个static实例的指针。函数创建此类的对象只能使用get_instance()函数,而此函数返回了一个static实例的指针,这就导致了每次调用此函数时返回的是同一个实例,从而实现单例。
除此之外还应该注意的是在get_instance()中使用new创建对象,所以是在堆上创建的,这样即使get_instance()函数退出,此对象也不会被释放。
有一种解决此问题的方法:可以在程序结束时调用get_instance(),并对返回的指针掉用delete操作。这样做可以实现功能,但不仅很丑陋,而且容易出错。因为这样的附加代码很容易被忘记,而且也很难保证在delete之后,没有代码再调用get_instance()函数。
一个妥善的方法是让这个类自己知道在合适的时候把自己删除,或者说把删除自己的操作挂在操作系统中的某个合适的点上,使其在恰当的时候被自动执行。

基于会产生内存泄漏的问题,下面提出一点改进之处:

class cfg_main_t
{
    public:
        static cfg_main_t* get_instance()
        {
            if(cfg_main == NULL)
                cfg_main = new cfg_main_t();
            return cfg_main;
        }
    private:
        cfg_main_t(){}
        static cfg_main_t cfg_main;
        class delete_instance_t
        {  
            public:  
            ~delete_instance_t()
            {  
                if(cfg_main_t::cfg_main)  
                    delete cfg_main_t::cfg_main);  
            }  
        };  
    static delete_instance_t delete_instance;   
};

对上述类的一些说明:
程序在结束的时候,系统会自动析构所有的全局变量。事实上,系统也会析构所有的类的静态成员变量,就像这些静态成员也是全局变量一样。利用这个特征,我们可以在单例类中定义一个这样的静态成员变量,而它的唯一工作就是在析构函数中删除单例类的实例。
程序运行结束时,系统会调用cfg_main_t的静态成员delete_instance的析构函数,该析构函数会删除单例的唯一实例。

进一步的讨论
但是添加一个类的静态对象,总是让人不太满意,所以有人用如下方法来重新实现单例和解决它相应的问题,代码如下:

class cfg_main_t
{  
private:  
    CSingleton()   //构造函数是私有的 
    {  
    }  
public:  
    static cfg_main_t& get_instance()  
    {  
        static cfg_main_t instance;   //局部静态变量 
        return instance;  
    }  
};  

使用局部静态变量,非常强大的方法,完全实现了单例的特性,而且代码量更少,也不用担心单例销毁的问题。
但使用此种方法也会出现问题,当如下方法使用单例时问题来了,
cfg_main_t cfg_main= cfg_main_t :: get_instance();
这么做就出现了一个类拷贝的问题,这就违背了单例的特性。产生这个问题原因在于:编译器会为类生成一个默认的构造函数,来支持类的拷贝。解决此办法的一个途径是显示定义类的拷贝构造函数和重载=运算符,这样就不会调用默认的这些函数了。

class cfg_main_t
{  
private:  
    cfg_main_t()   //构造函数是私有的 
    {  
    }  
    cfg_main_t (const cfg_main_t&);  //拷贝构造函数
    cfg_main_t& operator = (const cfg_main_t&);  //重载=运算符
public:  
    static cfg_main_t& get_instance()  
    {  
        static cfg_main_t instance;   //静态局部变量 
        return instance;  
    }  
};  

关于cfg_main_t (const cfg_main_t &);和 cfg_main_t & operate = (const cfg_main_t &);函数,需要声明成私有的,并且只声明不实现。这样,如果用上面的方式来使用单例时,不管是在友元类中还是其他的,编译器都是报错。项目中的消息类就是这样实现的。

你可能感兴趣的:(对象,设计)