C++设计模式整理005-单例模式

本文详细介绍了C++中单例模式的实现,包括懒汉单例(非线程安全、线程安全及返回局部静态对象的方式)和饿汉单例模式。同时,给出了一个涉及多线程安全的实例,展示了如何在单例模式中处理子类的创建。文章强调了单例模式的关键点,如构造函数私有化、全局唯一实例的获取等,并提供了相关的代码示例。
摘要由CSDN通过智能技术生成

目录

6. 单例模式

6.1 懒汉单例模式

6.2 饿汉单例模式

6.3 实例3


6. 单例模式

        单例模式顾名思义,保证一个类仅可以有一个实例化对象,并且提供一个可以访问它的全局接口。实现单例模式必须注意一下几点:

                1. 单例类只能由一个实例化对象。

                2. 单例类必须自己提供一个实例化对象。

                3. 单例类必须提供一个可以访问唯一实例化对象的接口。

        单例模式分为懒汉和饿汉两种实现方式。

6.1 懒汉单例模式

        懒汉:故名思义,不到万不得已就不会去实例化类,也就是说在第一次用到类实例的时候才会去实例化一个对象。在访问量较小,甚至可能不会去访问的情况下,采用懒汉实现,这是以时间换空间。

6.1.1 非线程安全的懒汉单例模式

/*
 * 关键代码:构造函数是私有的,不能通过赋值运算,拷贝构造等方式实例化对象。
*/

//懒汉式一般实现:非线程安全,getInstance返回的实例指针需要delete
class Singleton
{
public:
    static Singleton* getInstance();
    ~Singleton(){}

private:
    Singleton(){}                                        //构造函数私有
    Singleton(const Singleton& obj) = delete;            //明确拒绝
    Singleton& operator=(const Singleton& obj) = delete; //明确拒绝
    
    static Singleton* m_pSingleton;
};

Singleton* Singleton::m_pSingleton = NULL;

Singleton* Singleton::getInstance()
{
    if(m_pSingleton == NULL)
    {
        m_pSingleton = new Singleton;
    }
    return m_pSingleton;
}

6.1.2 线程安全的懒汉单例模式

std::mutex mt;

class Singleton
{
public:
    static Singleton* getInstance();
private:
    Singleton(){}                                    //构造函数私有
    Singleton(const Singleton&) = delete;            //明确拒绝
    Singleton& operator=(const Singleton&) = delete; //明确拒绝

    static Singleton* m_pSingleton;
    
};
Singleton* Singleton::m_pSingleton = NULL;

Singleton* Singleton::getInstance()
{
    if(m_pSingleton == NULL)
    {
        mt.lock();
        if(m_pSingleton == NULL)
        {
            m_pSingleton = new Singleton();
        }
        mt.unlock();
    }
    return m_pSingleton;
}

6.1.3 返回一个reference指向local static对象

        这种单例模式实现方式多线程可能存在不确定性:任何一种non-const static对象,不论它是local或non-local,在多线程环境下“等待某事发生”都会有麻烦。解决的方法:在程序的单线程启动阶段手工调用所有reference-returning函数。这种实现方式的好处是不需要去delete它。

class Singleton
{
public:
    static Singleton& getInstance();
private:
    Singleton(){}
    Singleton(const Singleton&) = delete;  //明确拒绝
    Singleton& operator=(const Singleton&) = delete; //明确拒绝
};


Singleton& Singleton::getInstance()
{
    static Singleton singleton;
    return singleton;
}

6.2 饿汉单例模式

        饿汉:饿了肯定要饥不择食。所以在单例类定义的时候就进行实例化。在访问量比较大,或者可能访问的线程比较多时,采用饿汉实现,可以实现更好的性能。这是以空间换时间。

//饿汉式:线程安全,注意一定要在合适的地方去delete它
class Singleton
{
public:
    static Singleton* getInstance();
private:
    Singleton(){}                                    //构造函数私有
    Singleton(const Singleton&) = delete;            //明确拒绝
    Singleton& operator=(const Singleton&) = delete; //明确拒绝

    static Singleton* m_pSingleton;
};

Singleton* Singleton::m_pSingleton = new Singleton();  // 在这里就实例化

Singleton* Singleton::getInstance()
{
    return m_pSingleton;
}

6.3 实例3

        单例的一般实现比较简单,下面是代码和UML图。由于构造函数是私有的,因此无法通过构造函数实例化,唯一的方法就是通过调用静态函数GetInstance

 

//Singleton.h
class Singleton  
{
public:
	static Singleton* GetInstance();
private:
	Singleton() {}
	static Singleton *singleton;
};
//Singleton.cpp
Singleton* Singleton::singleton = NULL;
Singleton* Singleton::GetInstance()
{
	if(singleton == NULL)
		singleton = new Singleton();
	return singleton;
}

        这里只有一个类,如何实现Singleton类的子类呢?也就说Singleton有很多子类,在一种应用中,只选择其中的一个。最容易就是在GetInstance函数中做判断,比如可以传递一个字符串,根据字符串的内容创建相应的子类实例。这也是DP书上的一种解法,书上给的代码不全。这里重新实现了一下,发现不是想象中的那么简单,最后实现的版本看上去很怪异。

//Singleton.h
#pragma once
#include <iostream>
using namespace std;
 
class Singleton  
{
public:
	static Singleton* GetInstance(const char* name);
	virtual void Show() {}
protected: //必须为保护,如果是私有属性,子类无法访问父类的构造函数
	Singleton() {}
private:
	static Singleton *singleton; //唯一实例的指针
};
 
//Singleton.cpp
#include "Singleton.h"
#include "SingletonA.h"
#include "SingletonB.h"
Singleton* Singleton::singleton = NULL;
Singleton* Singleton::GetInstance(const char* name)
{
	if(singleton == NULL)
	{
		if(strcmp(name, "SingletonA") == 0)
			singleton = new SingletonA();
		else if(strcmp(name,"SingletonB") == 0)
			singleton = new SingletonB();
		else 
			singleton = new Singleton();
	}
	return singleton;
}


//SingletonA.h
#pragma once
#include "Singleton.h"
class SingletonA: public Singleton
{
	friend class Singleton; //必须为友元类,否则父类无法访问子类的构造函数
public:
	void Show() { cout<<"SingletonA"<<endl; }
private:   //为保护属性,这样外界无法通过构造函数进行实例化
	SingletonA() {} 
};


//SingletonB.h
#pragma once
#include "Singleton.h"
class SingletonB: public Singleton
{
	friend class Singleton; //必须为友元类,否则父类无法访问子类的构造函数
public:
	void Show(){ cout<<"SingletonB"<<endl; }
private:  //为保护属性,这样外界无法通过构造函数进行实例化
	SingletonB() {}
};


#include "Singleton.h"
int main()
{
	Singleton *st = Singleton::GetInstance("SingletonA");
	st->Show();
	return 0;
}

        上面代码有一个地方很诡异,父类为子类的友元,如果不是友元,函数GetInstance会报错,意思就是无法调用SingletonA和SIngletonB的构造函数。父类中调用子类的构造函数,我还是第一次碰到。当然了把SingletonA和SIngletonB的属性设为public,GetInstance函数就不会报错了,但是这样外界就可以定义这些类的对象,违反了单例模式。

        看似奇怪,其实也容易解释。在父类中构建子类的对象,相当于是外界调用子类的构造函数,因此当子类构造函数的属性为私有或保护时,父类无法访问。为共有时,外界就可以访问子类的构造函数了,此时父类当然也能访问了。只不过为了保证单例模式,所以子类的构造函数不能为共有,但是又希望在父类中构造子类的对象,即需要调用子类的构造函数,这里没有办法才出此下策:将父类声明为子类的友元类。

 

参考资料:https://blog.csdn.net/wuzhekai1985/article/details/6665869

参考资料:https://www.cnblogs.com/chengjundu/p/8473564.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

公众号:程序喵星人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值