C++——类型转换与特殊类设计

我们在C语言中经常会使用到强制类型转换,例如指针和整形之间的转换是最为常见的,但是
在C++中,C++设计师认为这种强制类型转换是不安全的,所以在C++标准中加入了四种强制
类型转换风格,这就是我将要介绍的强制类型转换。
在某些场景中,我们可能需要一些特殊的类来让我们的代码能够更加符合场景,比如只能在栈
上创建对象,或者只能在堆上常见对象等等场景,而其中尤为出名的一种特殊类,也被纳入设计
模式中,那就是单例模式。

1. 特殊类设计

a. 不能能被拷贝的类

拷贝只会放生在两个场景中:拷贝构造函数以及赋值运算符重载,因此想要让一个类禁止拷贝,只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。
由于这两个函数是默认成员函数,你不写它会自动生成,所以我们需要显式地写出来,并且是具有私有属性:

class A
{
public:A(int a = 0):_a(a){}
private:A(const A& a);A& operator=(const A& a);int _a;
};

在这里插入图片描述
而在C++11中delete作用于函数有着新的作用,那就是不生成该函数,所以:

class A
{
public:A(int a = 0):_a(a){}A(const A& a) = delete;A& operator=(const A& a) = delete;
private:int _a;
};

这个的话私有共有无所谓了。
在这里插入图片描述

b. 只能在堆上创建的对象

我们想要创建一个对象无非就这两种方式:

int main()
{A aa;//栈上A* paa = new(A);//堆上return 0;
}

现在要让我们不能在栈上创建对象,一定是从构造函数找出发点,首先构造函数不能直接地被调用,说明他应该是私有成员,但是他是私有成员后,我们new一个对象的时候也需要构造函数,这样也不能在堆上创建对象了,这个时候我们就需要在类中再写一个函数,由于类内是没有私有公有一说的,所以类内成员函数可以随意的调用类内的任意成员函数,所以:

class A
{
public:static A* getObj(int a = 0){A* pa = new A(a);return pa;}
private:A(int a = 0) :_a(a){};int _a;
};

这样写完之后我们发现,我们要使用这个函数先得需要一个对象,但是我们又创建不了对象,自相矛盾了,所以我们需要将这个函数设置为静态的:

class A
{
public:static A* getObj(int a = 0){A* pa = new A(a);return pa;}
private:A(int a = 0) :_a(a){};int _a;
};

在这里插入图片描述
还有问题,如果是这样呢:
在这里插入图片描述
所以还需要禁用拷贝构造和赋值重载:

class A
{
public:static A* getObj(int a = 0){A* pa = new A(a);return pa;}
private:A(int a = 0) :_a(a){};A(const A& a) = delete;A& operator=(const A& a) = delete;int _a;
};

c. 只能在栈上创建对象

我们肯定还是在构造函数上找突破口:

class A 
{
public:static A getObj(){A a;return a;}private:A(int a = 0) :_a(a) {};int _a;
};

而对于这样的方式,有人会说会不会效率变慢了,如果对象需要在堆上开辟空间的话,不要忘了我们可是有右值引用的:
这个现象需要在VS2019或者更低版本的编译器上进行,VS2022优化有点高,看不到这种现象。

#include <iostream>
using namespace std;class A
{
public:static A getObj(){A a;return a;}A(A&& a){cout << "右值拷贝构造" << endl;swap(_a, a._a);}private:A(int a = 0) :_a(new int[a]) {};int* _a;
};int main()
{A a = A::getObj();//A* pa = new A;return 0;
}

在这里插入图片描述

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

关于这一个设计,这是一种设计模式,因为它经常出现在各种编程场景中,被人们广泛使用。
设计模式:

设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计
经验的总结。为什么会产生设计模式这样的东西呢?就像人类历史发展会产生兵法。最开
始部落之间打仗时都是人拼人的对砍。后来春秋战国时期,七国之间经常打仗,就发现打
仗也是有套路的,后来孙子就总结出了《孙子兵法》。孙子兵法也是类似。

使用设计模式的目的:

为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真
正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。

单例模式:

一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提
供一个访问它的全局访问点,该实例被所有程序模块共享。比如在某个服务器程序中,该
服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务
进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下
的配置管理。

而关于单例模式的设计有两种方法应对于不同的场景:

1). 饿汉模式

饿汉模式,他的意思就是在进入main函数之前,这个对象就已经被创建好了。
我们首先要想想,什么变量是在进入main函数之前就已经被创建好了呢?那肯定是全局变量啊,但是全局变量你要知道,它使用起来是有风险的,在多文件的时候尤为明显,可能会产生重定义的风险。所以我们是这样设计的:

class Singleton
{
public:static Singleton& getExa(){return sl;}void Add(const pair<string, int> kv){sl._hash.insert(kv);}void Print(){for (auto& e : sl._hash){cout << e.first << " : " << e.second << endl;}cout << endl;}
private:map<string, int> _hash;Singleton() {};Singleton(const Singleton& sl) = delete;Singleton& operator=(const Singleton& sl) = delete;static Singleton sl;
};Singleton Singleton::sl;int main()
{Singleton::getExa().Add({ "hello", 2 });Singleton::getExa().Add({ "world", 1 });Singleton::getExa().Add({ "xxx", 1 });Singleton::getExa().Add({ "yyy", 10 });Singleton::getExa().Print();return 0;
}

2). 懒汉模式

这种设计模式就是在对象被调用的时候再创建:

class Singleton
{
public:static Singleton* getExa(){if (sl == nullptr)sl = new Singleton;return sl;}void Add(const pair<string, int> kv){sl->_hash.insert(kv);}void Print(){for (auto& e : sl->_hash){cout << e.first << " : " << e.second << endl;}cout << endl;}
private:map<string, int> _hash;Singleton() {};Singleton(const Singleton& sl) = delete;Singleton& operator=(const Singleton& sl) = delete;static Singleton* sl;
};Singleton* Singleton::sl = nullptr;int main()
{Singleton::getExa()->Add({ "hello", 2 });Singleton::getExa()->Add({ "world", 1 });Singleton::getExa()->Add({ "xxx", 1 });Singleton::getExa()->Add({ "yyy", 10 });Singleton::getExa()->Print();return 0;
}

在饿汉模式中,对象的销毁在进程结束之后会自动销毁,并且由于是在栈上,所以无法手动销毁对象,但是在懒汉模式中该对象是从堆上开辟而来,有人就会想到,那这个对象究竟什么时候释放呢?程序也不会自动调用析构函数释放啊,其实我们不需要担心它内存泄露的问题,因为进程如果是正常退出的话,操作系统会帮我们做这一件事的,如果进程是异常退出的话,那需要担心的地方也不是这里了,而是为什么进程异常退出了。但是我们非要设计进程结束,释放资源的功能也是可以的:

class Singleton
{
public:static Singleton* getExa(){if (sl == nullptr)sl = new Singleton;return sl;}void Add(const pair<string, int> kv){sl->_hash.insert(kv);}void Print(){for (auto& e : sl->_hash){cout << e.first << " : " << e.second << endl;}cout << endl;}//创建一个内嵌的自定义类,并且创建该自定义类的栈上的对象,那么在进程结束的//时候该对象会调用它的析构函数,我们在它的析构函数中释放sl即可。class GC{public:~GC(){if (sl){delete sl;sl = nullptr;}}};private:map<string, int> _hash;Singleton() {};Singleton(const Singleton& sl) = delete;Singleton& operator=(const Singleton& sl) = delete;static Singleton* sl;static GC gc;
};Singleton* Singleton::sl = nullptr;
Singleton::GC Singleton::gc;

在这里插入图片描述

要注意:自定义类型的静态成员变量在类内声明,类外定义

3). 两者的场景

饿汉模式

如果这个单例对象在多线程高并发环境下频繁使用,性能要求较高,那么显然使用饿汉
模式来避免资源竞争,提高响应速度更好。

懒汉模式

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

在这里要说明一点,饿汉模式不需要担心线程安全问题,而懒汉模式需要,需要加锁,关于这一方面的问题,日后再谈(因为我也不会…)。

1. C语言类型转换

我们在编写C/C++代码的过程中肯定会不可避免地使用到强制类型转换,例如自己计算结构体某一成员偏移量,指针之间,指针与整形之间转换的场景我们都是这么写:

// 偏移量
#define offsetof( type,member) (char *)&((type *)0->member)

它的类型转换分为两种,一种是隐式的:

double j = 1.1;
int a = j;

但是很明显这种转换会丢失精度,还有就是显式的:

int main()
{int a = 0;int* pa = &a;int b = (int)pa;return 0;
}

对于以上类型转换会存在一些很明显的缺陷:

1. 转换的可视性比较差,所有的转换形式都是以一种相同形式书写,难以跟踪错误的转换
2.  隐式类型转化有些情况下可能会出问题:比如数据精度丢失
3. 显式类型转换将所有情况混合在一起,代码不够清晰
4. 隐式类型的转换在出现错误后一般在编译时才出现

所以基于这种情况C++标准提出了四种类型转换格式。

2. C++类型转换

a. static_cast

static_cast用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用static_cast,但它不能用于两个不相关的类型进行转换:

int main()
{double d = 12.34;int a = static_cast<int>(d);cout<<a<<endl;return 0;
}

b. 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;
}

c. const_cast

const_cast最常用的用途就是删除变量的const属性,方便赋值:

void Test ()
{const int a = 2;int* p = const_cast< int*>(&a );*p = 3;cout<<a <<endl;
}

d. dynamic_cast

dynamic_cast用于将一个父类对象的指针/引用转换为子类对象的指针或引用(动态转换)
向上转型:子类对象指针/引用->父类指针/引用(不需要转换,赋值兼容规则)
向下转型:父类对象指针/引用->子类指针/引用(用dynamic_cast转型是安全的)
注意:

  1. dynamic_cast只能用于父类含有虚函数的类
    1. dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0
class A
{
public :
virtual void f(){}
};
class B : public A
{};
void fun (A* pa)
{
// dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0
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;
}

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

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

相关文章

VS2022联合Qt5开发学习11(QT5.12.3联合VTK在VS2022上开发医学图像项目5——qvtkWidget上显示STL三维图像并取点)

这篇博文是接着这个系列前面的博文&#xff0c;来讲如何实现医学图像三视图同步视图。我想到的一个思路是用Scrollbar来控制切面的改变&#xff0c;还有一个想法是在三维图像上取点&#xff0c;然后以这个点为切面中心更新三维视图。这篇博文主要介绍的就是第二个想法的三维图像…

C++ qt标题栏组件绘制

本博文源于笔者在学习C qt制作的标题栏组件&#xff0c;主要包含了&#xff0c;最小化&#xff0c;最大化&#xff0c;关闭。读者在看到这篇博文的时候&#xff0c;可以直接查看如何使用的&#xff0c;会使用了&#xff0c;然后进行复制粘贴源码部分即可。 问题来源 想要制作…

支持向量机(Support Vector Machines)(需要优化)

1.优化目标 一个更加强大的算法广泛的应用于工业界和学术界&#xff0c;它被称为支持向量机(Support Vector Machine)。与逻辑回归和神经网络相比&#xff0c;支持向量机&#xff0c;或者简称 SVM&#xff0c;在学习复杂的非线性方程时提供了一种更为清晰&#xff0c;更加强大…

css 中 flex 布局最后一行实现左对齐

问题 flex 布局最后一行没有进行左对齐显示&#xff1a; <div classparent><div classchild></div><div classchild></div><div classchild></div><div classchild></div><div classchild></div><div…

2022年至2023年广东省职业院校技能大赛高职组“信息安全管理与评估”赛项样题

2022 年至 2023 年广东省职业院校技能大赛高职组“信息安全管理与评估”赛项样题 一、 第一阶段竞赛项目试题 本文件为信息安全管理与评估项目竞赛第一阶段试题&#xff0c;第一阶段内容包 括&#xff1a;网络平台搭建、网络安全设备配置与防护。 本阶段比赛时间为 180 分钟…

华为数通方向HCIP-DataCom H12-831题库(判断题:121-140)

第121题 BGP/MPLS IP VPN内层采用MP-BGP分配的标签区分不同的VPN实例,外层可采用多种隧道类型,例如GRE隧道。 正确 错误 答案: 错误 解析: VPN业务的转发需要隧道来承载,隧道类型包括GRE隧道、LSP隧道、TE隧道(即CR-LSP)。 如果网络边缘的PE设备具备MPLS功能,但骨干网核…

林浩然与Hadoop的奇幻数据之旅

林浩然与Hadoop的奇幻数据之旅 Lin Haoran and the Enchanting Data Journey with Hadoop 在一个名为“比特村”的地方&#xff0c;住着一位名叫林浩然的程序员大侠。他并非江湖上常见的武艺高强之人&#xff0c;而是凭借一把键盘、一支鼠标&#xff0c;纵横在大数据的海洋里。…

UI跟随物体的关键是什么?重要吗?

引言 UI的跟随效果 在游戏开发中&#xff0c;UI的跟随效果是提高用户体验和交互性的重要组成部分。 本文将深入介绍如何创建一个高效且可定制的UI跟随目标组件&#xff0c;并分享一些最佳实践。 本文源工程在文末获取&#xff0c;小伙伴们自行前往。 UI跟随物体的关键 UI…

MQ回顾之kafka速通

不定期更新 官网概念自查 官网&#xff1a;Apache Kafka kafka结构 和kafka相关的关键名词有&#xff1a;Producer、Broker、Topic、Partition、Replication、Message、Consumer、Consumer Group、Zookeeper。 各名词解释已经泛滥&#xff0c;如果你想看点不一样的&#xf…

A 股承担着一个什么功能?

​A 股&#xff1a;中国资本市场的核心角色 A 股&#xff0c;即人民币普通股票&#xff0c;在中国资本市场中扮演着至关重要的角色。它不仅是投资者买卖交易的场所&#xff0c;更是中国经济发展的重要引擎。 首先&#xff0c;A 股为中国的企业提供了融资平台。中国有着庞大的…

从Elasticsearch来看分布式系统架构设计

从Elasticsearch来看分布式系统架构设计 - 知乎 分布式系统类型多&#xff0c;涉及面非常广&#xff0c;不同类型的系统有不同的特点&#xff0c;批量计算和实时计算就差别非常大。这篇文章中&#xff0c;重点会讨论下分布式数据系统的设计&#xff0c;比如分布式存储系统&…

Zookeeper3.5.7源码分析

文章目录 一、Zookeeper算法一致性1、Paxos 算法1.1 概述1.2 算法流程1.3 算法缺陷 2、ZAB 协议2.1 概述2.2 Zab 协议内容 3、CAP理论 二、源码详解1、辅助源码1.1 持久化源码(了解)1.2 序列化源码 2、ZK 服务端初始化源码解析2.1 启用脚本分析2.2 ZK 服务端启动入口2.3 解析参…

鸿蒙入门学习的一些总结

前言 刚开始接触鸿蒙是从2023年开始的&#xff0c;当时公司在调研鸿蒙开发板能否在实际项目中使用。我们当时使用的是OpenHarmony的&#xff0c;基于DAYU/rk3568开发板&#xff0c;最开始系统是3.2的&#xff0c;API最高是API9&#xff0c;DevecoStudio 版本3.1的。 鸿…

excel统计分析——Duncan法多重比较

参考资料&#xff1a;生物统计学 Duncan法又称新复极差检验法&#xff0c;是对S-N-K法的改进&#xff0c;根据秩次距m对临界值的显著水平α进行调整&#xff0c;是最常用的多重比较方法。最小显著极差表示如下&#xff1a; 其中&#xff0c;m为秩次距&#xff0c;df为方差分析中…

【软件测试】学习笔记-制定一份有效的性能测试方案

什么是性能测试方案&#xff1f; 性能测试方案&#xff0c;通俗一点说就是指导你进行性能测试的文档&#xff0c;包含测试目的、测试方法、测试场景、环境配置、测试排期、测试资源、风险分析等内容。一份详细的性能测试方案可以帮助项目成员明确测试计划和手段&#xff0c;更…

第二集《闻法仪轨》

请大家打开讲义第三面&#xff0c;甲二、于法、法师发起承事。 我们身为一个大乘的佛弟子&#xff0c;我们这一念明了的心&#xff0c;在一生当中&#xff0c;会遇到很多很多的佛法&#xff0c;也会遇到很多很多的法师&#xff0c;但不是所有的法师跟佛法对我们都是帮助的&…

Prometheus插件安装kafka_exporter

下载地址 https://github.com/danielqsj/kafka_exporter/releases 解压 tar -zxvf kafka_exporter-1.7.0.linux-amd64.tar.gzmv kafka_exporter-1.7.0.linux-amd64 kafka_exporter服务配置 cd /usr/lib/systemd/systemvi kafka_exporter.service内容如下 [Unit] Descript…

概念性——数据库简介

前些天发现了一个人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;最重要的屌图甚多&#xff0c;忍不住分享一下给大家。点击跳转到网站。 概念性——数据库简介 介绍 数据对于当今许多应用程序和网站的运行至关重要。对热门视频的评论、多人游戏中分…

centos手动下载配置redis并自启动

有些服务器不能自动安装配置redis&#xff0c;仓库找不到之类的问题&#xff0c;就需要手动下载配置redis&#xff0c;记录下&#xff0c;方便以后使用&#xff08;ps&#xff0c;如果报错可能是gcc缺失&#xff09; 1、下载 Redis 源码包&#xff1a;访问 Redis 官网或可信的…

基于时空模型的视频异常检测

假设存在一个运动区域&#xff0c;规则要求只能进行特定的运动项目。 出于安全原因或因为业主不喜欢而禁止进行任何其他活动:)。 我们要解决的问题是&#xff1a;如果我们知道正确行为的列表&#xff0c;我们是否可以创建一个视频监控系统&#xff0c;在出现不常见的行为发出通…