C++特殊类设计【特殊类 || 单例对象 || 饿汉模式 || 懒汉模式】

目录

一,特殊类设计 

1. 只在堆上创建的类

2. 只允许在栈上创建的类 

3. 不能被继承的类

4. 不能被拷贝的类

 5. 设计一个类,只能创建一个对象(单例对象)

饿汉模式

懒汉模式

C++11静态成员初始化多线程安全问题

二, C++类型转换

C语言类型转换存在的问题

1. static_cast(近似类型)

2. reinterpret_cast(不相关类型)

3. const_cast 

4. dynamic_cast(一般用于多态)

5. RTTI(了解)

结语


嗨!收到一张超美的风景图,愿你每天都能顺心!

一,特殊类设计 

1. 只在堆上创建的类

实现方式:
1. 将类的构造函数私有,拷贝构造声明成私有。防止别人调用拷贝在栈上生成对象。
2. 提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建
// 方法一
class HeapOnly
{
public:void Destory(){cout << "~HeapOnly" << endl;delete this;}private:~HeapOnly(){}int i;
};
// 将析构函数设为私有,这样在栈中的对象(不需要手动析构),在栈结束时
// 无法自动调用析构函数。// 方法二:构造函数私有,仅提供一个creat函数来进行构造
class HeapOnly_2
{
public:// static 添加原因:我们知道调用成员函数需要对象,但我们这时并没有对象,// 因此需要用static修饰。(先有鸡先有蛋问题)static HeapOnly_2*  creat_(int b) {return new HeapOnly_2(b);}// 防止默认拷贝,赋值构造生成,在栈上创建HeapOnly_2(const HeapOnly_2& b) = delete;HeapOnly_2& operator=(const HeapOnly_2& b) = delete;
private:HeapOnly_2(int b):i(b){}int i;
};int main()
{// HeapOnly it;HeapOnly* it = new HeapOnly;it->Destory();//HeapOnly_2 it2;HeapOnly_2* it2 = HeapOnly_2::creat_(1);//HeapOnly_2 it3 = *it2; //HeapOnly_2 it3(*it2);delete it2return 0;
}

2. 只允许在栈上创建的类 

class StackOnly
{
public:static StackOnly creat_(int b){return StackOnly(b); //意味着拷贝构造不能禁止 }// creat_返回值是右值,我们拷贝构造参数只设置右值,同时默认构造也不会产生StackOnly(StackOnly&& st):_i(st._i){}//但这也没有禁止,move一下就又可以了private:// 构造一私有,堆栈上都无法构造创建StackOnly(int i):_i(i){}StackOnly& operator=(const StackOnly& st) = delete;int _i;
};

方法同1类似,但这无法限制住,static对象的拷贝。

3. 不能被继承的类

C++98方式,将构造函数私有,派生类无法调用到构造函数导致无法被继承。

// C++98中构造函数私有化,派生类中调不到基类的构造函数。则无法继承
class NonInherit
{
public:static NonInherit GetInstance(){return NonInherit();}
private:NonInherit(){}
};

C++11新方式,在目标类添加关键词——final,表示该类无法被继承

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

4. 不能被拷贝的类

拷贝只会放生在两个场景中:拷贝构造函数以及赋值运算符重载,因此 想要让一个类禁止拷贝,只需让该类不能调用拷贝构造函数以及赋值运算符重载即可
C++98
将拷贝构造函数与赋值运算符重载只声明不定义,并且将其访问权限设置为私有即可
class CopyBan
{// ...private:CopyBan(const CopyBan&);CopyBan& operator=(const CopyBan&);//...
};
原因:
1. 设置成私有:如果只声明没有设置成private,用户自己如果在类外定义了,就可以不能禁止拷贝了
2. 只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写反而还简单,而且如果定义了就不会防止成员函数内部拷贝了。

C++11
C++11扩展delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟上=delete,表示让编译器删除掉该默认成员函数。
class CopyBan
{// ...CopyBan(const CopyBan&)=delete;CopyBan& operator=(const CopyBan&)=delete;//...
};

 5. 设计一个类,只能创建一个对象(单例对象)

设计模式:
设计模式(Design Pattern)是一套 被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。我们学过的有适配器,迭代器,包装器,以及扩展:工厂,观察者模式。下面的单例模式也是
单例模式:
一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理。单例模式有两种实现模式:

饿汉模式

就是说不管你将来用不用,程序启动时就先创建一个唯一的实例对象
class SingleCase
{
public:static SingleCase* GetInstace(){return _sc;}size_t GetSize(){return _vc.size();}void Add(const string& str){_mtx.lock();_vc.push_back(str);_mtx.unlock();}void print(){for (auto& it : _vc){cout << it << endl;}}SingleCase(const SingleCase& it) = delete;SingleCase& operator=(const SingleCase& it) = delete;
private:SingleCase(){}mutex _mtx;vector<string> _vc;static SingleCase* _sc;
};
// 声明定义分离,_sc是也在类成员中(能调用类成员),虽然看起来像在类外。
SingleCase* SingleCase::_sc = new SingleCase;

 饿汉的缺点:

1. 单例对象的构造可能会占用大量资源,对应用程序的打开比较慢。

2. 不适合多个单例对象之间存在依赖关系生成。如单例A,单例B,必须得先有A再有B,如果在同一个源文件中跟声明顺序相同;但如果不在同一个源文件中,产生顺序就不确定。

优点:代码简单,支持一些高并发场景

懒汉模式

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

class SingleCase_lazy
{
public:static SingleCase_lazy* GetInstace(){// 处理大多数情况,提高效率if (_sc == nullptr){SingleCase_lazy::new_mtx.lock();// 两程序几乎同一时间进入该// 作用域时,再进行判断if (_sc == nullptr) {// 加锁防止_sc发生覆盖_sc = new SingleCase_lazy;}SingleCase_lazy::new_mtx.unlock();}return _sc;}size_t GetSize(){return _vc.size();}void Add(const string& str){_vmtx.lock();_vc.push_back(str);_vmtx.unlock();}void print(){for (auto& it : _vc){cout << it << endl;}}static void Destory(){new_mtx.lock();if (_sc){cout << "delete _sc" << endl;}new_mtx.unlock();delete _sc;}class De{public:~De(){Destory();}};SingleCase_lazy(const SingleCase_lazy& it) = delete;SingleCase_lazy& operator=(const SingleCase& it) = delete;
private:SingleCase_lazy(){}static mutex new_mtx;mutex _vmtx;vector<string> _vc;static SingleCase_lazy* _sc; //无法显示调用析构,我们通过内部类,调用
};
SingleCase_lazy* SingleCase_lazy::_sc = nullptr;
mutex SingleCase_lazy::new_mtx; // 静态函数的锁
SingleCase_lazy::De _de;

总结:设计一个单例对象,我们需要满足:1. 把数据放一个类中,把这个类设计成单例类。 2. 保持进程唯一,需要将拷贝,赋值都禁止。 3. 设计模式是饿汉,还是懒汉

C++11静态成员初始化多线程安全问题

class SingleInstance {
public:static SingleInstance* GetInstance() {// 局部静态变量实现的懒汉式单例,C++11后线程安全// 在C++11之前,不能保证多线程安全static SingleInstance instance;return &instance;}
private:SingleInstance();~SingleInstance();SingleInstance(const SingleInstance& signal) = delete;SingleInstance& operator=(const SingleInstance& signal) = delete;
};

二, C++类型转换

标准C++为了加强类型转换的可视性,引入了四种命名的强制类型转换操作符:
static_cast、reinterpret_cast、const_cast、dynamic_cast

 

C语言类型转换存在的问题

C风格的转换格式很简单,但是有不少缺点的:
1. 隐式类型转化有些情况下可能会出问题:比如数据精度丢失
2. 显式类型转换将所有情况混合在一起,代码不够清晰
因此C++提出了自己的类型转化风格,注意因为C++要兼容C语言,所以C++中还可以使用C语言的转化风格

1. static_cast(近似类型)

static_cast用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用static_cast,但它不能用于两个不相关的类型进行转换。
int main()
{double d = 12.34;int a = static_cast<int>(d);cout<<a<<endl;return 0;
}

2. reinterpret_cast(不相关类型)

reinterpret_cast操作符通常为操作数的位模式提供较低层次的重新解释,用于将一种类型转换为 另一种不同的类型
int main()
{double d = 12.34;int a = static_cast<int>(d);cout << a << endl;// 这里使用static_cast会报错,应该使用reinterpret_cast//int *p = static_cast<int*>(a);int *p = reinterpret_cast<int*>(a);return 0;
}

3. const_cast 

const_cast 最常用的用途就是删除变量的 const 属性,方便赋值
int main()
{volatile const int i = 10;int* it = const_cast<int*>(&i);*it = 2;cout << i << endl;  // 10cout << *it << endl; // 2// i为什么没有被修改?答:i被编译器优化,i被10替换到寄存中,并没有在内存中获取//在C++中,volatile关键字用于告诉编译器该变量的值可能会在程序的控制之外被改变,//因此编译器不应该对该变量进行优化。这意味着每次访问该变量时,编译器都会从内存// 中读取最新的值,而不是使用之前缓存的值 (来自gpt的回答)return 0;
}

4. dynamic_cast(一般用于多态)

dynamic_cast 用于将一个父类对象的指针 / 引用转换为子类对象的指针或引用 ( 动态转换 )

 

注意:
1. dynamic_cast 只能用于父类含有 虚函数 的类。

 2. dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0。

class A
{
public :
virtual void f(){}
};
class B : public A
{};
void fun (A* pa)
{
// dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回
B* pb1 = static_cast<B*>(pa);
B* pb2 = dynamic_cast<B*>(pa);
cout<<"pb1:" <<pb1<< endl;
cout<<"pb2:" <<pb2<< endl;
}
int main ()
{A a;B b;fun(&a);fun(&b);return 0;
}

5. RTTI(了解)

RTTI Run-time Type identification 的简称,即: 运行时类型识别
C++ 通过以下方式来支持 RTTI
1. typeid 运算符(获取类型)
2. dynamic_cast 运算符
3. decltype

结语

   本小节就到这里了,感谢小伙伴的浏览,如果有什么建议,欢迎在评论区评论,如果给小伙伴带来一些收获请留下你的小赞,你的点赞和关注将会成为博主创作的动力

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/734572.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux安装ngnix完整步骤(支持centos/银河麒麟操作系统)

linux安装ngnix&#xff08;支持centos/银河麒麟操作系统&#xff09; 本次操作系统安装ngnix采用离线或在线安装方式&#xff0c;离线就是不联网环境&#xff0c;在线则是联网环境&#xff1b;支持centos7或centos8或国产操作系统&#xff08;银河麒麟高级服务器操作系统&…

element-ui radio 组件源码分享

今日简单分享 radio 组件的实现原理&#xff0c;主要从以下三个方面来分享&#xff1a; 1、radio 页面结构 2、radio 组件属性 3、radio 组件方法 一、radio 页面结构 1.1 页面结构如下&#xff1a; 二、radio 属性 2.1 value / v-model 属性&#xff0c;类型为 string / …

鲜为人知的闰年判定大坑

【题目描述】 输入年份&#xff0c;判断是否为闰年。如果是&#xff0c;则输出yes&#xff0c;否则输出no。 提示&#xff1a;简单地判断除以4的余数是不够的。 【题目来源】 刘汝佳《算法竞赛入门经典 第2版》习题1-7 年份&#xff08;year&#xff09; 【解析】 一、闰…

Decontam去污染:一个尝试

为了程序运行的便利性&#xff0c;不想将Decontam放到windows的Rstudio里面运行&#xff0c;需要直接在Ubuntu中运行&#xff0c;并且为了在Decontam时进行其他操作&#xff0c;使用python去运行R 首先你需要有一个conda环境&#xff0c;安装了R&#xff0c;Decontam&#xff0…

云计算的部署方式(公有云、私有云、混合云、社区云)

云计算的部署方式(公有云、私有云、混合云、社区云) 目录 零、00时光宝盒 一、云计算的部署方式 1.1、公有云&#xff08;Public Cloud&#xff09; 1.2、私有云&#xff08;Private Cloud&#xff09;  1.3、混合云&#xff08;Hybrid Cloud&#xff09; 1.4、社区云&am…

【C++】list模拟实现list迭代器失效问题

list模拟实现&list迭代器失效问题 一&#xff0c;list模拟实现1. list的主要框架接口模拟2. list构造&拷贝构造&析构3. list迭代器3.1 普通迭代器3.2 const迭代器 4. 增删查改 二&#xff0c;迭代器失效问题1. list的迭代器失效原因2. 解决办法 一&#xff0c;list…

Java 汇编源码查看环境搭建

目录 一、简介 二、在IDEA开发环境中搭建汇编环境 2.1 在IDEA中搭建字节码查看环境 2.1.1 搭建步骤 2.1.1.1 第一步 2.1.1.2 第二步 2.1.1.3 第三步 2.1.1.4 第四步 2.1.2 验证 2.2 在IDEA开发环境中搭建汇编代码查看环境 2.2.2 配置HSDIS插件 2.2.3 验证HSDIS插件是…

[虚拟机保护逆向] [HGAME 2023 week4]vm

[虚拟机保护逆向] [HGAME 2023 week4]vm 虚拟机逆向的注意点&#xff1a;具体每个函数的功能&#xff0c;和其对应的硬件编码的*长度* 和 *含义*&#xff0c;都分析出来后就可以编写脚本将题目的opcode转化位vm实际执行的指令 &#xff1a;分析完成函数功能后就可以编写脚本输出…

深度学习在硬件和计算平台上的优化:实现更快、更高效的突破

引言 深度学习&#xff0c;作为机器学习领域的一个子集&#xff0c;通过模拟人脑神经元的连接方式&#xff0c;构建复杂的网络结构来处理和分析数据。然而&#xff0c;随着深度学习模型规模的不断扩大和复杂度的提高&#xff0c;其对计算资源的需求也呈指数级增长。因此&#…

【MySQL】表的增删改查——MySQL基本查询、数据库表的创建、表的读取、表的更新、表的删除

文章目录 MySQL表的增删查改1. Create&#xff08;创建&#xff09;1.1 单行插入1.2 多行插入1.3 替换 2. Retrieve&#xff08;读取&#xff09;2.1 select查看2.2 where条件2.3 结果排序2.4 筛选分页结果 3. Update&#xff08;更新&#xff09;3.1 更新单个数据3.2 更新多个…

如何保证消息的可靠传输

数据的丢失问题&#xff0c;可能出现在生产者、MQ、消费者中 生产者丢失&#xff1a; 生产者将数据发送到 RabbitMQ 的时候&#xff0c;可能数据就在半路给搞丢了&#xff0c;因为网络问题啥的&#xff0c;都有可能。此时可以选择用 RabbitMQ 提供的事务功能&#xff0c;就是生…

Unmanaged PowerShell

简介 在渗透测试当中经常会使用到PowerShell来执行脚本, 但是直接使用PowerShell.exe是一个非常敏感的行为, EDR等产品对PowerShell.exe进程的创建监控的很密切, 并且随着PowerShell的渗透测试工具的普及, 越来越多的EDR会利用微软提供的AMSI接口对PS脚本进行扫描, 但是对于低…

vue实现购物车功能

实现功能 CSS部分 <style>.tr {display: flex;}.th {margin: 10px;width: 20%;height: 50%;}.td {display: flex;margin: 10px;width: 20%;height: 100px;align-items: center;}.app-container .banner-box {border-radius: 20px;overflow: hidden;margin-bottom: 10px;}…

input中文输入法导致的高频事件

这是基本结构 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>中文输入法的高频事件</title&…

通信-CAN-01 总线拓扑

本文主要介绍CAN总线拓扑&#xff0c;并结合实际用到CAN设备做些说明。 1 总线拓扑 拓扑结构中分为CPU&#xff0c;CAN 控制器&#xff0c;收发器&#xff0c;双绞线。CAN控制器根据两根线上的电位差来判断总线电平。发送方通过使总线发生变化&#xff0c;将消息发送给接收方…

BPSK调制解调

BPSK数字调制是相移键控PSK的一种&#xff0c;通过数字信号&#xff0c;调制载波的相位&#xff0c;利用载波的相位变化来反映数字信号&#xff0c;载波的振幅和频率均不变化。PSK应用很广泛&#xff0c;抗噪声性能比ASK和FSK要好&#xff0c;频带利用率较高。BPSK中&#xff0…

前端文件流、切片下载和上传

前端文件流、切片下载和上传技术在提升文件传输效率和优化用户体验方面发挥着关键作用。这些技术不仅可以帮助解决大文件传输过程中可能遇到的问题&#xff0c;如网络超时、内存溢出等&#xff0c;还能通过并行传输和断点续传等功能&#xff0c;提高传输速度和稳定性。 一、前端…

每日学习笔记:C++ 11的Tuple

#include <tuple> Tuple介绍(不定数的值组--可理解为pair的升级版) 定义 创建 取值 初始化 获取tuple元素个数、获取tuple某元素类型、将2个tuple类型串接为1个新tuple类型

解决Ubuntu 16.04/18.04 图形化界面异常、鼠标光标消失、鼠标变成叉叉等问题

bug场景&#xff1a; 一切从一次换源说起…叭叭叭 这篇文章解决的问题&#xff1a; 1.换源&#xff0c;默认源太慢&#xff0c;换成可用的阿里云的源 2.apt-get failed to …问题 3.图形化异常问题 4.get unmet dependence 问题 5. 鼠标光标消失和鼠标变成叉叉问题。 解决方…

【Python】time模块

专栏文章索引&#xff1a;Python 目录 一、介绍​编辑 二、常用函数​编辑 一、介绍 Python 的 time 模块提供了处理时间的函数。 二、常用函数 1.time()&#xff1a;返回当前时间的时间戳&#xff08;从1970年1月1日开始计时的秒数&#xff09;。 import timecurrent_ti…