单例模式:一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。
只在堆/栈的类
请设计一个类,该类只能在堆上创建对象
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函数开始之后创建对象,存在线程安全问题。 | 线程安全问题,占用的资源更多(锁就是很大代价的资源) |