C++单例模式

单例模式:一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。

只在堆/栈的类

请设计一个类,该类只能在堆上创建对象

1、将类的构造函数私有,拷贝构造声明私有。 防止别人调用拷贝在栈上生成对象。
2. 提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建。

#include<iostream>
using namespace std;


class OnlyHeap
{
public:
    static OnlyHeap* Get()
    {
        return new OnlyHeap;
    }
private:
    OnlyHeap(){}//构造函数
    //OnlyHeap(const OnlyHeap&);//拷贝构造          C++98
    OnlyHeap(const OnlyHeap&)=delete;//拷贝构造     C++11支持
};

int main()
{
    OnlyHeap* a=OnlyHeap::Get();//定义一个指针去指向Get()函数返回值
}

自定义类型创建就肯定会调用构造函数,而我们将构造函数包装成Get()函数,Get()是静态成员函数,它不属于对象,它存储在静态区。这里也能体现C++的封装思想,将构造函数和拷贝构造函数通过private封装。

请设计一个类,该类只能在栈上创建对象

class StackOnly
{
public:
	static StackOnly CreateObject()
	{
		return StackOnly();
	}
private:
	void* operator new(size_t size);
	void operator delete(void* p);
};

饿汉模式

程序启动时就创建单例对象

#include<iostream>
using namespace std;

class Single
{
public:
	static Single* Get()
	{
		return &_data;
	}
private:
	static Single _data;
private:
	Single() {};//构造
	Single(const Single&) = delete;//拷贝构造
	Single& operator=(const Single&) = delete;//赋值运算
};
Single Single::_data;//程序运行前就创建了对象

int main()
{
	Single* f = Single::Get();
}

饿汉模式不存在线程安全问题,因为在main函数执行前,对象已经创建好了。为什么这里类中可以定义对象自己呢?因为static成员变量,它被存储在静态区,可以说它不属于对象本身。而static修饰的变量只有一份,每次无论谁来调用我的Get函数,我给你的只是我这唯一对象的指针。

懒汉模式

同饿汉模式相同的是,都采用的是static特性,实现单例,不同在于,对象创建的时间,对于懒汉模式,main函数运行起来,需要对象时候,GetObj()函数发现还没有对象,这个时候才创建对象。

正由于懒汉模式创建对象在main开始以后,所以,如果多个线程都来创建对象,那么就存在资源争抢问题。定义互斥变量锁来对创建对象过程进行加锁操作。

static Back* GetObj()
	{
		if (_data == nullptr)//如果没有创建对象才让线程去拿锁
		{
			_mutex.lock();
			if (_data == nullptr)
				_data = new Back();//创建对象
			_mutex.unlock();
		}
		return _data;
	}

在创建对象前进行了两次判断是因为在加锁之前判断,如果对象已经创建,那么就不给线程锁资源了,直接给线程返回对象指针。如果两个线程同时到来,都判断到此时对象没有创建,只有一个线程拿到了锁,那么拿到锁的线程,创建完对象,刚刚没拿锁到的锁的线程又会去创建对象,所以拿到锁之后要进行第二次判断对象是否已经创建。

#include<iostream>
#include<mutex>
#include<thread>
using namespace std;

class Single
{
public:
	class GC//内嵌类---垃圾回收
	{
	public:
		~GC()
		{
			if (_data)
				delete _data;
		}
	};

	static Single* GetObj()
	{
		if (_data == nullptr)//如果没有创建对象才让线程去拿锁
		{
			_mutex.lock();
			if (_data == nullptr)
				_data = new Single();//创建对象
			_mutex.unlock();
		}
		return _data;
	}
private:
	Single() {}//构造函数
	Single(const Single&) = delete;//拷贝构造
	Single& operator=(const Single&) = delete;//赋值运算符
private:
	static Single* _data;
	static mutex _mutex;
	static GC _gc;
};

Single* Single::_data = nullptr;
mutex Single::_mutex;
Single::GC Single::_gc;

void Func()
{
	cout << Single::GetObj() << endl;
}
int main()
{
	thread t1(Func);
	thread t2(Func);
	thread t3(Func);
	thread t4(Func);
	t1.join();
	t2.join();
	t3.join();
	t4.join();

	Func();
	return 0;
}
000B8F40
000B8F40
000B8F40
000B8F40
000B8F40
请按任意键继续. . .
设计模式性质缺点
饿汉模式在main函数开始之前就创建了唯一对象,不存在线程安全问题,可以用于高并发场景频繁使用可能会导致启动缓慢
懒汉模式在main函数开始之后创建对象,存在线程安全问题。线程安全问题,占用的资源更多(锁就是很大代价的资源)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值