【C++进阶】特殊类设计 单例模式

server/2024/9/23 11:16:14/

目录

  • 不能被拷贝的类
  • 只能在堆上创建对象
  • 只能在栈上创建对象
  • 不能被继承的类
  • 只能创建一个对象(单例模式
    • 饿汉模式
    • 懒汉模式

不能被拷贝的类

//C++98
class CopyBan
{
public:private:CopyBan(const CopyBan&){};const CopyBan& operator=(const CopyBan&){};
};//C++11
class CopyBan
{
public:CopyBan(const CopyBan&) = delete;const CopyBan& operator=(const CopyBan&) = delete;
private:};

C++98做法的原因:

  1. 设置成私有:如果只声明没有设置成private,用户自己如果在类外定义了,就可以不能禁止拷贝了
  2. 只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写反而还简单,而且如果定义了就不会防止成员函数内部拷贝了。

C++11就直接删除该默认成员函数!

只能在堆上创建对象

实现原理:

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

代码实现:

class HeapOnly
{
public:static HeapOnly* CreateObjeckt(){return new HeapOnly;}HeapOnly(const HeapOnly&) = delete;HeapOnly& operator=(const HeapOnly&) = delete;
private:HeapOnly() {};
};int main()
{//都被禁止创建栈上的对象/*HeapOnly h1;static HeapOnly h2;HeapOnly* h3 = new HeapOnly;*/HeapOnly* h4 = HeapOnly::CreateObjeckt();return 0;
}

只能在栈上创建对象

同上将构造函数私有化,然后设计静态方法创建对象返回即可。

class StackOnly
{
public:static StackOnly CreateObjeckt(){return StackOnly();}void* operator new(size_t size) = delete;void operator delete(void* p) = delete;
private:StackOnly(){};
};int main()
{// 禁掉operator new可以把下面用new 调用拷贝构造申请对象给禁掉StackOnly obj1 = StackOnly::CreateObjeckt();//StackOnly* obj2 = new StackOnly(obj1);return 0;
}

不能被继承的类

C++98方式
C++98中构造函数私有化,派生类中调不到基类的构造函数。则无法继承

//C++98方式
class NonInherit // Inherit继承
{
public:static NonInherit GetInstance(){return NonInherit();}
private:NonInherit(){};
};

C++11方式
final关键字,final修饰类,表示该类不能被继承。

class A final
{
// ....
};

只能创建一个对象(单例模式

饿汉模式

原理:

  1. 一开始就创建对象(main函数之前)
  2. 只创建一个对象并且是私有静态成员对象,禁止拷贝构造和赋值重载。
  3. 提供全局的访问点

代码实现:

class PeopleInfo
{
public:static PeopleInfo* GetInstance(){return &_pinfo;}private:PeopleInfo(){};PeopleInfo(const PeopleInfo&) = delete;PeopleInfo& operator=(const PeopleInfo&) = delete;
private:string _name = "xxx";int _age = 0;//...//声明static PeopleInfo _pinfo;
};//定义
PeopleInfo PeopleInfo::_pinfo;int main()
{cout << PeopleInfo::GetInstance() << endl;cout << PeopleInfo::GetInstance() << endl;cout << PeopleInfo::GetInstance() << endl;return 0;
}

优点:简单
问题:

  1. main函数之前就创建对象
  2. 假如同时创建很多单例类,有些单例对象初始化资源很多,导致程序启动缓慢,迟迟不进入main函数。
  3. 如果两个单例类有初始化依赖关系,比如A类和B类是单例,A类要连接数据库,B单例要用A单例访问数据库,那谁先初始化呢,我们无法确定!
    所以就有了懒汉模式

懒汉模式

如果单例对象构造十分耗时或者占用很多资源,比如加载插件啊,
初始化网络连接啊,读取文件啊等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化,就会导致程序启动时非常的缓慢。
所以这种情况使用懒汉模式(延迟加载)更好。

懒汉模式完美解决了上述问题
原理:

  1. 第一次调用GetInstance时创建对象。
  2. 构造依旧私有,拷贝构造和赋值重载删除掉。
  3. 提供全局的访问点。

代码实现:

class PeopleInfo
{
public:static PeopleInfo* GetInstance(){static PeopleInfo pinfo;return &pinfo;}private:PeopleInfo(){};PeopleInfo(const PeopleInfo&) = delete;PeopleInfo& operator=(const PeopleInfo&) = delete;
private:string _name = "xxx";int _age = 0;//...
};int main()
{cout << PeopleInfo::GetInstance() << endl;cout << PeopleInfo::GetInstance() << endl;cout << PeopleInfo::GetInstance() << endl;return 0;
}

但是上述代码又会带来新的问题,假如是在多线程情况下跑呢?
我们就需要考虑线程安全的问题!!!
上述写法C++11之前不能用,局部静态单例对象构造初始化无法保证线程安全问题,C++11后可以用,C++11之后进行了优化,就不会出现线程安全的问题了!
但是要更稳妥,我们还是对代码进行一些优化
C++11之前也能保证是线程安全的:
代码实现:

#include<mutex>
class PeopleInfo
{
public:static PeopleInfo* GetInstance(){if (_pinfo == nullptr)//保证性能,下次线程再进来就不会重复加锁了!!!{unique_lock<mutex> lock(_mtx);//保证线程安全if (_pinfo == nullptr){_pinfo = new PeopleInfo;}}return _pinfo;}private:PeopleInfo(){};PeopleInfo(const PeopleInfo&) = delete;PeopleInfo& operator=(const PeopleInfo&) = delete;
private:string _name = "xxx";int _age = 0;//...static PeopleInfo* _pinfo;static mutex _mtx;
};PeopleInfo* PeopleInfo::_pinfo = nullptr;
mutex PeopleInfo::_mtx;
int main()
{cout << PeopleInfo::GetInstance() << endl;cout << PeopleInfo::GetInstance() << endl;cout << PeopleInfo::GetInstance() << endl;return 0;
}

http://www.ppmy.cn/server/96154.html

相关文章

基于R语言绘制GGE双标图2

参考资料&#xff1a; 严威凯等: 双标图分析在农作物品种多点试验中的应用【作物学报】 https://cran.r-project.org/web/packages/GGEBiplots/GGEBiplots.pdf 1、如何判断双标图是否充分体现数据中的规律 在对双标图的解释中&#xff0c;有一个隐含的假设&#xff0c;就是所…

密码学简史:时间密语

​ 注&#xff1a;机翻&#xff0c;未校。 A brief history of cryptography: Sending secret messages throughout time Stemming from the Greek words for “hidden writing,” cryptography is the practice of encrypting transmitted information so that it can only b…

openGauss 5.0 LTS部署至华为云ECS CentOS8.2实操教程

一、前言 openGauss是一款高可靠、高性能、高安全、易运维的开源关系型数据库管理系统&#xff0c;然而其全功能部署对系统要求非常高。 本实操教程能够使个人开发者以及高校师生能够以成本最小的方式快速将openGauss部署到华为云的ECS上&#xff0c;以便快速进行功能验证以及…

STK12.2+Python开发(三):通过Access获取当前场景的信息,计算卫星访问目标信息

计算访问 在STK中&#xff0c;“Access”通常指的是卫星与地面目标之间的可见性或访问机会。在STK API中&#xff0c;Access分析功能允许用户计算和分析特定时间段内卫星与地面目标之间的访问窗口。这些访问窗口可以用来确定卫星何时能够观测或通信目标。 一个Access能够retu…

ARM知识点二

一、指令 指令的生成过程 指令执行过程示例 if (a 0) {x 0; } else {x x 3; } //翻译为 cmp r0,#0 MOVEQ R1,#0 ADDGT R1,R1,#3指令获取&#xff1a;从Flash中读取 CMP R0, #0&#xff0c;控制器开始执行。 指令解码&#xff1a;解码器解析 CMP 指令&#xff0c;ALU比较R…

RHEL9网络设定及网络脚本

1. 添加一张网卡 2. 重写一个网卡配置文件 [rootlocalhost ~]# cd /etc/NetworkManager/system-connections/ [rootlocalhost system-connections]# ls ens160.nmconnection [rootlocalhost system-connections]# vim ens224.connection [rootlocalhost system-connections…

在亚马逊云科技AWS上利用PEFT和RLHF高效微调AI大模型减少有害回复

简介&#xff1a; 小李哥将继续每天介绍一个基于亚马逊云科技AWS云计算平台的全球前沿AI技术解决方案&#xff0c;帮助大家快速了解国际上最热门的云计算平台亚马逊云科技AWS AI最佳实践&#xff0c;并应用到自己的日常工作里。 本次我将介绍如何用亚马逊云科技的AI模型训练服…

org.springframework.web.client.HttpClientErrorException$NotFound异常

springCloud报错信息&#xff1a;org.springframework.web.client.HttpClientErrorException$NotFound: 404 null第一点&#xff1a; 第二点&#xff1a;没有httpclient工具类 注入RestTmeplate类时&#xff0c;改类需要RestController或ResponseBody