C++常见设计模式之单例模式(如何实现+懒汉+饿汉)

1、如果我们想要别人按照我们的规定去完成事情,我们需要完成两个功能,第一把所有能够完成这件事情的方法禁止,第二提供一个唯一的我们的方法。

2、单例模式:设计一个类 这个类以后只能存在一个对象

1)将构造函数设置为私有方法,这样其他部分的代码就无法通过调用该类的构造函数
来实例化该类的对象。然后只能通过该类提供的一个静态方法来获得该类的唯一实例。

2)在该类内提供一个静态方法,当我们调用这个方法时,如果类所拥有的指针指向不为空则返回这个指针,如果类所持有的指针指向为空则创建该类的实例,并将类所拥有的指针指向该实例。

class Cperson
{
    
    
private:
	Cperson()
	{
    
    

	}
private:
	static Cperson* ps;

public:
	static Cperson* GetObject();
};
Cperson* Cperson::GetObject()  //获取对象的接口
{
    
    
		if(ps == 0)
		{
    
    
			ps = new Cperson;
		}
		return ps;
}
Cperson* Cperson::ps = 0;   //用来标记程序中存在的唯一对象 (也需要是类中的私有静态变量)如果是简单的全局变量 随处都可修改这个标记

int main()
{
    
    
     Cperson* p1 = Cperson::GetObject();

	//简单的全局变量 ps = 0; 这时又会创造出一个对象 所以要在类里为私有的 静态是因为静态函数不能使用非静态变量 而且 如果不是静态的变量 需要对象进行初始化 
                //但是对象构造出来还需要这个变量  所以将这个变量声明为静态的在类外通过类名加作用域的方式进行初始化 不通过对象 
	
	Cperson* p2 = Cperson::GetObject();
	
	return 0;  //p1 p2的指向将会是同一块空间
}

3、为什么接口要是静态方法?
因为一般类内的方法需要对象来调用,可是我们已经私有化构造函数,只能通过提供的这个唯一的接口去实例化对象,可是我们现在没有对象,又需要使用这个方法,所以将他声明为static。静态成员/方法可以通过类名作用域直接使用 ,因为在编译时期就存在,所有对象共享,只有这一份,且静态函数只能用静态成员。

4、使用场景
资源管理,如线程池、日志等。
在多线程条件下实现单例模式如果不采取任何措施,那么该实现是不安全的,可能会创建多个单例。所以一般情况,其实现通常有两种方式:懒汉式与饿汉式。

考虑到两个线程同时首次调用到GetObject方法,且同时检测到p指针为NULL的时候,则两个线程会同时构造一个实例给p。这是严重的错误。

5、懒汉与饿汉
单例的两种实现方法
1)懒汉:看名字就知道 不到万不得已就会去实例化类 也就是说在第一次用到类实例的时候才会去实例化,所以经典的单例模式的实现方法为懒汉模式。在访问量比较小的时候采用懒汉,以时间换空间。
2)饿汉:饿了肯定要饥不择食,所以在单例模式类定义的时候就要进行实例化。
由于要进行线程同步,所以在访问量比较大的时候,或者可能访问的线程比较多,采用饿汉实现,以空间换时间。

6、线程安全的懒汉模式
线程不安全 怎么办呢 最直观的方法就是加锁 (互斥锁)
方法一:加锁的经典懒汉模式

class Cperson
{
    
    
private:
	Cperson()
	{
    
    
		pthread_mutex_init(&mutex);

	}
private:
	static Cperson* ps;
	static pthread_mutex_t mutex;
public:
	static Cperson* GetObject();
	
};

Cperson* Cperson::ps = 0;  
pthread_mutex_t Cperson::mutex;
Cperson* Cperson::GetObject() 
{
    
    
	if(ps == 0)
	{
    
    
		 pthread_mutex_lock(&mutex);
		 if(ps  == 0)
		 ps = new Cperson;
		 pthread_mutex_unlock(&mutex);
	}
	return ps;
}

方法二:内部静态变量的懒汉实现
在GetObject函数里定义一个静态的实例,在返回时返回其指针。

class Cperson
{
    
    
private:
	Cperson()
	{
    
    
		pthread_mutex_init(&mutex);

	}
private:
	static pthread_mutex_t mutex;
public:
	static Cperson* GetObject();
	
};

pthread_mutex_t Cperson::mutex;

Cperson* Cperson::GetObject()  
{
    
    
       pthread_mutex_lock(&mutex);
       static Cperson obj;
       pthread_mutex_unlock(&mutex);
       
       return &obj;
}

7、饿汉实现
饿汉谈不到线程安全,本身就是线程安全的。

#include<iostream>
using namespace std;

class Person{
    
    
private:
	Person(){
    
    

	}
private:
	static Person *p;

public:
	static Person* GetObject();
};

Person *Person::p = new Person;

Person *Person::GetObject()
{
    
    
	return p;
}
int main()
{
    
    
	Person * p1 = Person::GetObject();

	Person * p2 = Person::GetObject();

	return 0;
}

内容部分参考:https://blog.csdn.net/ddllrrbb/article/details/78215277
https://blog.csdn.net/qq_38917783/article/details/102839098

猜你喜欢

转载自blog.csdn.net/scarificed/article/details/121292019