当前位置:网站首页>单例模式和特殊类设计
单例模式和特殊类设计
2022-06-10 13:40:00 【你算哪一个bug?】
单例模式
什么是单例模式
单例模式(Singleton),保证一个类仅有一个实例,并提供一个访问它的全局访问点。–大话设计模式
应用场景
保证一个类只有一个实例
如Windows下的任务管理器,回收站等。
日志管理,计数器等。
简而言之,你需要唯一实例时就可以考虑单例模式。这样它可以严格地控制客户怎样访问即何时访问它,即对唯一实例的受控访问。
优缺点
优点
- 减少内存开销,因为在系统中只有一个实例。
- 避免了频繁的创建和销毁对象,提高了性能
- 避免对资源的多重占用,比如单例时多人只写一个日志文件,如果有多个日志文件可能导致对相同的日志文件进行写操作
- 设置全局访问点
缺点
- 职责过重,与单一职责存在冲突
- 不能继承(构造方法私有)
实现
单例模式有两种实现模式,懒汉模式和饿汉模式。
注意单例模式的概念,大概就是唯一实例且有全局访问点,我们从这两点入手。
唯一实例:构造函数私有+防拷贝
拷贝构造是一种构造方式,所以需要防止,构造函数私有不让别人new。
全局访问点:给一个公共的接口
饿汉模式
简单来说,把东西一开始就做好,需要的时候直接吃。
#include <iostream>
using namespace std;
class Singleton
{
public:
static Singleton& GetInstance()
{
return _instance;
}
int GetRandom()//由于侧重点不是随机数 所以直接返回一个30
{
return 30;
}
private:
Singleton() {}//构造函数私有
Singleton(Singleton&) = delete;//防拷贝,被=delete修饰表明这个函数被删除,即可以只声明不实现,换言之禁用了该函数
Singleton& operator=(const Singleton&) = delete;//防拷贝
static Singleton _instance;
};
Singleton Singleton::_instance;//类外必须初始化,类内只是声明
int main()
{
//1.调用
cout<<Singleton::GetInstance().GetRandom()<<endl;
//2.
Singleton& s = Singleton::GetInstance();
cout<<s.GetRandom()<<endl;
return 0;
}
通过防拷贝和构造函数私有化之后下面的几种办法都失效了
Singleton s;//err
Singleton s1(s);//err
Singleton s1=s;//err
从上面我们可以看出饿汉模式的优缺点了,有点显而易见就是实现很简单粗暴,缺点很明显,类加载时单例对象就已经生成了,即还没有用就已经加载出来了,比如这个资源很大,又在游戏启动时加载,那就会造成游戏启动很慢。且如果有多个单例对象启动时实例化顺序不确定(不同源文件类内的单例对象实例化顺序是不确定的,懒汉模式解决了这个问题,因为懒汉模式的实例化在函数内部,可以通过调用函数的顺序来解决实例化的顺序问题)。
类加载时静态初始化解决了线程安全问题。
懒汉模式
什么时候需要就什么时候做饭,然后吃。
class Singleton
{
public:
static Singleton* GetInstance()
{
if (_instance == nullptr)
{
_mtx.lock();//double lock保证线程安全
if (_instance == nullptr)//必须再次检查 不然可能另一个线程那已经new完了,这边又new违背了单例,且可能覆盖数据。
{
_instance = new Singleton();
}
_mtx.unlock();
}
return _instance;
}
int GetRandom()
{
return 30;
}
class Clear//资源回收的内部类,必须是公有的,不然外部声明报错
{
public:
~Clear()
{
cout << "释放资源" << endl;
delete _instance;
}
};
private:
static Clear _cle;
Singleton() {}
Singleton(Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static Singleton* _instance;
static mutex _mtx;//线程安全
};
Singleton* Singleton::_instance = nullptr;
mutex Singleton::_mtx;
Singleton::Clear _cle;
int main()
{
//1.
cout<<Singleton::GetInstance()->GetRandom()<<endl;
//2/
static Singleton* s = Singleton::GetInstance();
cout << s->GetRandom() << endl;
//不能用左值引用接收的原因 返回值是右值 得用右值引用接收
//为什么函数返回值是右值 因为返回值是借助临时变量返回的拿不到地址
//如果返回值是左值引用就能用左值引用接收
return 0;
}
懒汉模式的优缺点也很明显,优点是什么时候第一次用就什么时候实例化,此外可以通过调用函数解决多个单例对象实例化的顺序问题,缺点就是写起来复杂,要考虑线程安全和内存泄露方面的问题。懒汉采用的是指针也更好回收资源(饿汉采用的是对象)
懒汉可能因为多线程丢数据,线程加锁保证在多线程环境下一定只有一个线程去new对象,只创建出一个单例对象,加锁可能导致频繁切换上下文,double lock解决。
特殊类设计
我们一般会通过构造函数,拷贝构造和赋值重载创建对象,现在把这几种方法全禁用了,然后自己再写一个外部可调用的接口来自定义类的创建方式。
当然也有别的办法,这种经常可以作为一种通解思路。
下面的禁用采用C++11的delete关键字实现,作用是禁止编译器生成默认的函数版本,即有声明但无实现。
设计一个类只能在堆上创建对象
简单来说只能通过new来创建对象。
方法一
构造方法禁用,然后给一个接口让外部调用。
构造方法的禁用=构造函数私有+禁用拷贝构造+禁用赋值重载
class HeapOnly
{
public:
static HeapOnly* GetInstance()
{
return new HeapOnly;
}
void Test()
{
cout << "I am Test" << endl;
}
private:
HeapOnly() {}
HeapOnly(HeapOnly&) = delete;
HeapOnly& operator=(const HeapOnly&) = delete;
};
int main()
{
HeapOnly* ho = HeapOnly::GetInstance();
ho->Test();
delete ho;
return 0;
}

方法二
析构函数私有
对象建立在栈上,是由编译器分配空间的,编译器管理对象的生命周期,对象使用完后编译器会检查这个对象所有的非静态函数,包括析构函数,当编译器发现析构函数不能访问后就不能回收这块空间,所以编译器无法为其分配空间,编译器检查到这种情况也会报错。
析构函数私有的方法不建议使用,因为在类外无法使用delete释放空间,容易造成内存泄漏。
class HeapOnly
{
public:
void Test()
{
cout << "I am Test" << endl;
}
private:
~HeapOnly();
};
int main()
{
//HeapOnly ho_stack;//err
HeapOnly* ho = new HeapOnly;
ho->Test();
return 0;
}

只能在栈上创建对象
方法一
不能使用new --> 重载operator new即可。
这种存在一个缺陷,就是仍然可以在静态区创建对象

方法二
将构造函数设为私有再自定义一个接口
这里不用禁用构造函数,拷贝构造,赋值重载,因为对于下面的场景来说,匿名对象的拷贝构造更符合场景,编译器会选拷贝构造来进行构造,所以如果我们禁用了拷贝构造会报错,因为我们的delete关键字是有声明无实现,并不是真的把这个函数连带声明删除了。所以StackOnly()一看有我们自己写的拷贝构造的声明就会去匹配这个拷贝构造,我们自己写的拷贝构造又没有实现拷贝功能就会报错。
理解这个和编译链接知识有关,编译器看到声明就去匹配了,而不是一定要看到函数实现才匹配,链接时候才会去找实现,当发现有声明无实现时就很容易导致链接错误。

一个类不能被继承
父类构造函数私有即可,构造时先构造父类再构造子类,父类构造不出不能继承。
最后
关于单例模式,可以说是只能创建一个对象的实现。
还有个小问题,为什么不用全局变量代替单例模式,全局定义一个唯一的变量不就行了,合理吧[doge],理论上可以,但是非常不建议,可以自行查找资料"为什么不建议使用全局变量"。
全局变量的使用可能带来很多问题,而且很容易造成链接、重定义等错误,如果多人协作时有人再给这个变量起个别名,那维护代码时代价就太大了,这还只是全局变量的一个小缺点。
牛客代码规范评分里也不建议用全局变量,当然写题时代码没那么长,几个全局变量倒时不打紧。
.h不能包含定义,不然多个cpp去包含就会有链接错误,尽量把定义和声明分离开来
边栏推荐
- Resolve the error reported when installing gerapy: error: cannot uninstall 'certificate' It is a distutils installed project...
- [golang] when creating a structure with configuration parameters, how should the optional parameters be transferred?
- leetcode-56-合并区间
- 《软件体系结构原理、方法与实践》第二版期末考试复习总结
- Analysis on the use of coordinatorlayout
- 【FAQ】運動健康服務REST API接口使用過程中常見問題和解决方法總結
- In depth analysis of "circle group" relationship system design | series of articles on "circle group" technology
- 【无标题】
- D:\setup Exe could not find the problem
- 技术分享| 快对讲,全球对讲
猜你喜欢

【笔记】关于keil中的出现的编译映射内存不足的问题

Application analysis of key recording and playing of wt2003h4-16s voice chip

Solve the problem of cross sea high concurrent crash? so easy

The relocation of Apple's production line shows that 5g industrial interconnection and intelligent manufacturing have limited help for manufacturing in China

Mmdetection adds precision to the evaluation index

架构实战营 第 6 期 模块八课后作业

如何定位游戏发热问题

32. Simple test of raspberry pie serial port communication and ultrasonic module ranging
![[笔记]Windows安全之《三》Shellcode 补充之 Get-InjectedThread脚本搭建环境及其使用](/img/b4/f7838a7e12379190e2bc9b869839f0.png)
[笔记]Windows安全之《三》Shellcode 补充之 Get-InjectedThread脚本搭建环境及其使用

40 necessary methodologies for large factories
随机推荐
[yellow code] SVN version control tutorial
[cloud computing] what is the relationship between a multi cloud management platform and a public cloud?
工作中记录MySQL中的常用函数
Docker部署一个Redis集群
【技术分析】探讨大世界游戏的制作流程及技术——前期流程篇
Google Earth Engine(GEE)——基于s2影像的实时全球10米土地利用/土地覆盖(LULC)数据集
What are the common automated test frameworks? Shanghai software testing company Amway
D:\setup Exe could not find the problem
MarkDown 标题居中
Celery 异步调用方法改动记录
新功能|Mail GPU Counter模块新增GPU图元处理和GPU Shader Cycles
Markdown sets the font to red
「大模型」之所短,「知识图谱」之所长
CoordinatorLayout使用浅析
[Huang ah code] Why is php7 twice as fast as PHP5?
TabLayout 使用详解(修改文字大小、下划线样式等)
【笔记】74HC573的一些记录
[Multisim Simulation] differential amplifier circuit 2
Tablayout usage details (modify text size, underline style, etc.)
Qt: 访问其他窗体中的控件