当前位置: 首页 > news >正文

C++:特殊类设计和四种类型转换

一、特殊类设计

1.1 不能被拷贝的类

          拷贝只会放生在两个场景中:拷贝构造函数以及赋值运算符重载,因此想要让一个类禁止拷贝,只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。

C++98:

1、将拷贝构造函数与赋值运算符重载只声明不定义。(防自己人)

        不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写反而还简单,而且如果定义了就可能导致成员函数进行内部拷贝了。

2、并且将其访问权限设置为私有即可。(防外人)

        如果只声明没有设置成private,用户自己如果在类外定义了,就可以不能禁止拷贝了

class CopyBan
{
// ...
private:
CopyBan(const CopyBan& c);
CopyBan& operator=(const CopyBan& c);
//...
};

C++11:

       C++11扩展delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟=delete,表示让编译器删除掉该默认成员函数。

class CopyBan
{
// ...
CopyBan(const CopyBan& c)=delete;
CopyBan& operator=(const CopyBan& c)=delete;
//...
};

1.2 只能在堆上创建对象的类

思路1:

1. 将类的构造函数私有,拷贝构造声明成私有(可以直接delete掉)。防止别人调用拷贝在栈上生成对象。

      注意:拷贝构造可以直接delete掉,但是构造函数不行!!因为我们还需要利用构造函数在堆上创建对象。
2. 提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建。

     注意:这里涉及到的是先有鸡还是先有蛋的问题,因为如果不去创建这个对象就没有办法去调用他的构造函数,但是没有调用构造函数就没有办法创建对象。所以这里必须通过静态成员函数的返回值去构造堆对象。

class HeapOnly
{
public:static HeapOnly* CreateObject(){return new HeapOnly;}
private:HeapOnly(){};HeapOnly(const HeapOnly&) = delete;
};int main()
{HeapOnly*p = HeapOnly::CreateObject();return 0;
}

思路2:

1、相比较于上一种思路把构造函数私有、拷贝构造delete,也可以选择将析构函数给设成私有。

      析构函数设置成私有同样会导致对象无法在栈上进行创建。因为自定义类型在栈帧中销毁的时候会去自动调用他的析构函数,但是因为调不到所以会报错。

2、封装一个destory的成员函数,这样我们可以手动释放堆空间的资源。

      在堆上创建对象是用指针去接受,所以并不影响,但是内存需要我们去手动释放,因此我们需要封装destory的成员函数去调用delete,这样delete可以对应调用到析构函数。 这里有两个方案:一种是设置能静态成员函数(类域调用),一种是设置成普通成员函数(对象自己调用)。

class HeapOnly
{
public://方案1 static void Destroy(HeapOnly* hp){delete hp;}//方案2void Destroy(){delete this;}
private:~HeapOnly() {};
};int main()
{//HeapOnly H; 调不到析构函数,无法创建//静态成员函数释放HeapOnly* ptr = new HeapOnly;HeapOnly::Destroy(ptr);//普通成员函数释放HeapOnly* ptr2 = new HeapOnly;ptr2->Destroy();return 0;
}

1.3 只能在栈上创建对象的类

1、new和delete是全局的运算符重载函数,因此我们只要将这两个给禁用掉,就不会在堆上创建对象。

      需了解具体声明:void* operator new(size_t size) 和 void operator delete(void* p) 。实现类专属的operator new和delete 这样new这个类对象时,operator new就会调用这个,不会调全局的。

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

但是没有办法去禁用C语言的相关函数。 

1.4 不能被继承的类

C++98:

构造函数私有化,这样子类调用不到父类的构造函数,无法实现继承

class NonInherit
{
public:
static NonInherit GetInstance()
{
return NonInherit();
}
private:
NonInherit()
{}
};

C++11:

直接用final关键字,可以使得该类无法被继承

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

1.5 控制可创建对象数量的类

设置一个静态int变量count,每创建一个对象就--count

class singleclass
{
public:static singleclass* getsingleclass(){if (count > 0){count--;return new singleclass();}else{cout << "实例化失败" << endl;return nullptr;}}
private:static int count;singleclass() {};
};
int singleclass::count = 1;int main()
{singleclass* s1 = singleclass::getsingleclass();singleclass* s2 = singleclass::getsingleclass();
}

二、类型转换

2.1 C语言中的类型转换

        在C语言中,如果赋值运算符左右两侧类型不同,或者形参与实参类型不匹配,或者返回值类型与接收返回值类型不一致时,就需要发生类型转化,C语言中总共有两种形式的类型转换:隐式类型转换和显式类型转换。


1、隐式类型转换:编译器在编译阶段自动进行,能转就转,不能转就编译失败。相近类型才可以进行隐式类型转换 比如int和double 他们本质上都是表示数据的大小。

int i = 1;
// 隐式类型转换
double d = i;

 double d=i :首先会产生一个double类型的临时对象接收i,然后将临时对象拷贝给d。

2、显式类型转化:需要用户自己处理。需要有一定的关联性。比如int和int*

int i = 1;
int* p = &i;
// 显示的强制类型转换
int address = (int)p;
printf("%x, %d\n", p, address);
return 0;

缺陷:转换的可视性比较差,所有的转换形式都是以一种相同形式书写,难以跟踪错误的转换

2.2 C++中的类型转换

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

2.2.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.2.2 reinterpret_cast

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

2.2.3 const_cast

       const_cast最常用的用途就是删除变量的const属性,方便做赋值操作(const_cast的类型必须是对象的指针或者引用)

int main()
{const int a = 2;int* p = const_cast<int*>(&a);*p = 3;cout << a << endl;cout << *p << endl;cout << &a << endl;cout << p << endl;//cout的缺陷char ch = 'x';cout << &ch << endl;return 0;
}

思考:为什么*p被修改了,a却没有被修改??

       因为常量被存到寄存器中了,所以其实改变的是内存中的a,但是不是寄存器中的a。这其实是一种优化,如果我们想要去掉这种优化,用volatile关键字(告诉编译器不要优化,直接从内存中读取) 

2.2.4 dynamic_cast(针对父类指针或引用的向下转型)

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

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

思考:

1、为什么父类和子类明明是两种类型,但是向上转型不需要转换呢??

class A
{
public:virtual void f() {}int _a = 0;
};class B : public A
{public:int _b = 1;
};int main()
{B objb;A obja = objb;A& ra = objb;double d = 1.1;const int& i = d;return 0;
}

2、为什么向下转型是不安全的??

      因为子类的指针或引用如果是父类对象,那么会存在一部分的越界!!!

3、为什么 dynamic_cast只能用于父类含有虚函数的类

        dynamic_cast转换是在运行时进行转换,因为只有对于这种类层次结构,才应该将派生类的地址赋给基类指针。运行时转换就需要知道类对象的信息(继承关系等)。C++对象模型中,对象实例最前面的就是虚函数表指针,通过这个指针可以获取到该类对象的所有虚函数,包括父类的。因为派生类会继承基类的虚函数表,所以通过这个虚函数表,我们就可以知道该类对象的父类,在转换的时候就可以用来判断对象有无继承关系。

  所以虚函数对于正确的基类指针转换为子类指针是非常重要的。

4、dynamic_cast的使用原理

       父类指针或引用如果本来指向的是子类的对象,那么类型转化是安全的,如果原本指向的是父类的对象,那么转化是不安全的(有越界,但是编译器检查不出来),所以dynamic_cast可以帮助我们去判断这种情况,如果不符合安全转化的条件,就会返回nullptr。

void fun(A* pa)
{//  向下转换:直接转换是不安全的// 如果pa是指向父类A对象,存在越界问题B* ptr = dynamic_cast<B*>(pa);if (ptr!=nullptr){ptr->_a++;ptr->_b++;}else{cout << "转换失败" << endl;}
}int main()
{// 向下转换规则:父类对象不能转换成子类对象,但是父类指针和引用可以转换子类指针和引用B b1;A a;B b;fun(&a);fun(&b);return 0;
}

2.3 为什么C++需要四种类型转换

C风格的转换格式很简单,但是有不少缺点的:
1、隐式类型转化有些情况下可能会出问题:比如数据精度丢失

2、显式类型转换将所有情况混合在一起,代码不够清晰

3、为了提供更安全、更明确的类型转换,使得代码意图更为清晰

       因此C++提出了自己的类型转化风格,注意因为C++要兼容C语言,所以C++中还可以使用C语言的转化风格。因此C++的转化风格并非强制性的,只不过是程序员之间的一种规范。

2.4 RTTI

RTTI:Run-time Type identification的简称,即:运行时类型识别。RTTI只适用于包含虚函数的类
C++通过以下方式来支持RTTI:
1. typeid运算符(返回指出对象类型的值)
2. dynamic_cast运算符(根据虚基表判断有无继承关系,并判断是否可以转化)
3. decltype(推断表达式返回值的类型)

2.5 相关面试题

1. C++中的4中类型转化分别是:_________、_________、_________、________

2.描述四种类型转化各自的应用场景。


相关文章:

C++:特殊类设计和四种类型转换

一、特殊类设计 1.1 不能被拷贝的类 拷贝只会放生在两个场景中&#xff1a;拷贝构造函数以及赋值运算符重载&#xff0c;因此想要让一个类禁止拷贝&#xff0c;只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。 C98&#xff1a; 1、将拷贝构造函数与赋值运算符重载只…...

(南京观海微电子)——屏幕材质及优缺点对比

LED/LCD LCD&#xff08;Liquid Crystal Ddisplay&#xff09;即“液晶显示器”&#xff0c;由两块偏光镜、两块薄膜晶体管以及彩色滤光片、光源&#xff08;荧光灯&#xff09;、显示面板组成的成像元器件。 LED&#xff08;Light Emitting Diode&#xff09;即“发光二极管…...

uniapp uni.showModal 出现点击没有反应

uni.showModal 里面有好些参数 点击后不弹出 是因为 出现了 null 或者undifind 字符 特别是content 里面 title: 提示, cancelColor: #000000, editable: true,//是否显示输入框 content: item.text?item.te…...

Vue3-VueRouter

客户端 vs. 服务端路由​ 服务端路由指的是服务器根据用户访问的 URL 路径返回不同的响应结果。当我们在一个传统的服务端渲染的 web 应用中点击一个链接时&#xff0c;浏览器会从服务端获得全新的 HTML&#xff0c;然后重新加载整个页面。 然而&#xff0c;在单页面应用中&a…...

【图像处理与机器视觉】频率域滤波

知识铺垫 复数 CRjI 可以看作复平面上的点&#xff0c;则该复数的坐标为&#xff08;R&#xff0c;I&#xff09; 欧拉公式 e j θ c o s θ j s i n θ e^{j\theta} cos \theta j sin \theta ejθcosθjsinθ 极坐标系中复数可以表示为&#xff1a; C ∣ C ∣ ( c o s…...

python第五次作业

1.请实现一个装饰器&#xff0c;每次调用函数时&#xff0c;将函数名字以及调用此函数的时间点写入文件中 # 导入datetime模块&#xff0c;用于获取当前时间并格式化输出 import datetime# 定义一个装饰器工厂函数log_funcName_time&#xff0c;它接受一个参数time def log_fu…...

JS面向对象编程

目录 实例对象与new命令this关键字对象的原型和继承Object对象的相关方法浅拷贝和深拷贝严格模式实例对象与new命令 构造函数 构造函数的特点有两个: 函数体内部使用了this关键字,代表了所要生成的对象实例。生成对象的时候,必须使用new命令。var Vehicle...

kotlin1.8.10问题导致gson报错TypeToken type argument must not contain a type variable

书接上回&#xff0c;https://blog.csdn.net/jzlhll123/article/details/139302991。 之前我发现gson报错后&#xff1a; gson在2.11.0给我的kotlin项目代码报错了。 IllegalArgumentException: TypeToken type argument must not contain a type variable 上次解释原因是因为&…...

数据库漫谈-国产数据库

国产数据库突然大量出现&#xff0c;下表列出&#xff08;按首字母排序 &#xff09; AISWare AntDB&#xff0c;亚信科技。 AliSQL&#xff0c;阿里云。 Analyticdb&#xff0c;阿里云。 ArkDB&#xff0c;北京极数云舟科技有限公司。 CynosDB&#xff0c;腾讯云 DM&…...

小白跟做江科大32单片机之光敏传感器控制蜂鸣器

代码部分 1.思路 通过光敏电阻&#xff0c;控制蜂鸣器的发声 2.butter.h代码 #ifndef _BUTTER__H #define _BUTTER__H void butter_Init(void); void butter_on(void); void butter_off(void); #endif 3.butter.c代码 #include "stm32f10x.h" void butter…...

使用 Django Channels 构建实时聊天应用(包含用户认证和消息持久化)

文章目录 准备工作创建 Django 项目创建应用程序配置项目编写 Consumer编写路由创建 URL 路由运行应用用户认证消息持久化显示历史消息结论 Django Channels 是 Django 的一个扩展&#xff0c;允许在 Web 应用中添加实时功能&#xff0c;例如 Websockets、HTTP2 和其他协议。本…...

【Elasticsearch】es基础入门-03.RestClient操作文档

RestClient操作文档 示例&#xff1a; 一.初始化JavaRestClient &#xff08;一&#xff09;引入es的RestHighLevelClient依赖 <!--elasticsearch--> <dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest…...

LeetCode - 二分查找(Binary Search)算法集合(Python)[左右边界|旋转数组|双列表]

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/139419653 二分查找&#xff0c;也称为折半查找&#xff0c;是一种在有序数组中查找特定元素的高效算法。其基本原理是将待搜索的区间分成两半&am…...

android睡眠分期图

一、效果图 做医疗类项目&#xff0c;经常会遇到做各种图表&#xff0c;本文做的睡眠分期图。 二、代码 引入用到的库 api joda-time:joda-time:2.10.1 调用代码 /*** 睡眠* 分期*/private SleepChartAdapter mAdapter;private SleepChartAttrs mAttrs;private List<SleepI…...

2023年信息素养大赛小学组C++智能算法复赛真题

今天给大家分享2023年全国青少年信息素养大赛小学组C智能算法挑战赛复赛里面的一套真题&#xff0c;希望有助于大家了解复赛的难度及备考。 其他真题下载&#xff1a;网盘-真题-信息素养大赛...

独立游戏开发的 6 个步骤

&#x1f482; 个人网站:【 摸鱼游戏】【神级代码资源网站】【工具大全】&#x1f91f; 一站式轻松构建小程序、Web网站、移动应用&#xff1a;&#x1f449;注册地址&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交…...

Stable Diffusion AI绘画:从创意词汇到艺术图画的魔法之旅

文章目录 一、Stable Diffusion的工作原理二、从提示词到模型出图的过程三、Stable Diffusion在艺术创作中的应用《Stable Diffusion AI绘画从提示词到模型出图》内容简介作者简介楚天 目录前言/序言本书特色特别提示 获取方式 在科技的飞速发展中&#xff0c;Stable Diffusion…...

使用C++实现高效的套接字连接池

在现代网络应用中&#xff0c;高效管理网络连接是实现高并发和低延迟的重要因素。下面将详细介绍如何使用C实现一个高效的套接字连接池&#xff0c;以便在需要时快速复用连接&#xff0c;从而提高系统性能和资源利用率。 一、什么是连接池&#xff1f; 连接池是一种管理网络连…...

个人百度百科怎么创建

编辑百度词条是一个相对简单的流程&#xff0c;但需要注意的是&#xff0c;并不是所有的词条都可以编辑&#xff0c;部分锁定的词条是无法编辑的&#xff0c;但可以通过官方平台申请解封。以下百科优化网yajje分享是详细的步骤&#xff1a; 注册百度账号 首先&#xff0c;用户…...

Nvidia Jetson/Orin +FPGA+AI大算力边缘计算盒子:潍柴雷沃智慧农业无人驾驶

潍柴雷沃智慧农业科技股份有限公司&#xff0c;是潍柴集团重要的战略业务单元&#xff0c;旗下收获机械、拖拉机等业务连续多年保持行业领先&#xff0c;是国内少数可以为现代农业提供全程机械化整体解决方案的品牌之一。潍柴集团完成对潍柴雷沃智慧农业战略重组后&#xff0c;…...

ICPC训练赛补题集

ICPC训练赛补题集 文章目录 ICPC训练赛补题集D - Fast and Fat (负重越野)I-路径规划G. Inscryption(邪恶铭刻)NEW Houses雪中楼(西安交通大学)L.BracketGenerationE - Checksum D - Fast and Fat (负重越野) 原题链接&#xff1a;原题链接 题意&#xff1a;体重大的背体重小的…...

The First项目报告:解读去中心化衍生品交易所AVEO

2023 年12月8日凌晨&#xff0c;Solana 生态 MEV 基础设施开发商 Jito Labs 开放了 JTO 空投申领窗口&#xff0c;JTO 的价格在开盘短暂震荡后迅速攀高&#xff0c;一度触及 4.94 美元。 JTO 是加密社区这两日关注的热门标的&#xff0c;而在这场讨论中&#xff0c;除 Solana …...

Docker 快速更改容器的重启策略(Restart Policies)以及重启策略详解

目录 1. 使用 docker update 命令2. 在启动容器时指定重启策略3. 在 Docker Compose 文件中指定重启策略4. 总结 官方文档&#xff1a;Start containers automatically 1. 使用 docker update 命令 Docker 提供了 docker update 命令&#xff0c;可以在容器运行时更改其重启策…...

docker 启动关闭,设置仓库地址

1. 配置/etc/docker/daemon.json cat /etc/docker/daemon.json# 内容 {"registry-mirrors": ["https://0nth4654.mirror.aliyuncs.com"],"insecure-registries": ["harbor.domain.io"] }2. 配置systemd启动文件 和方法1配置会有冲突&a…...

二叉树的链式结构实现

前言 该篇是在二叉树介绍及堆-CSDN博客的基础上的。该篇会有点抽象大家要自己多画画图自己感受一下。现在我们开始吧&#xff01; 在学习二叉树基本操作时&#xff0c;我们需要先有一个现成的二叉树。来方便我们练习。因为现在我们对二叉树的理解也并不是很深入。在这里创建一个…...

MySQL远程连接

文章目录 MySQL远程连接(Linux)一、更改MySQL配置文件二、进入MySQL修改用户表host值三、使用其他电脑即可远程访问数据库MySQL远程连接(Linux)一、修改my.ini中的配置文件二、修改用户权限三、远程连接 MySQL远程连接(Linux) 以下MySQL远程连接&#xff1a;MySQL部署环境为Ubu…...

奔驰大G升级电动踏板效果

奔驰大G车型的升级旋转电动踏板是一项非常实用的功能&#xff0c;它为驾驶者提供了诸多便利和舒适性。以下是关于这一功能的实用性介绍&#xff1a; 便利的上下车体验&#xff1a;旋转电动踏板可以在车辆停稳的情况下自动伸出&#xff0c;为乘客提供便利的上下车体验。特别是对…...

【xilinx】vivado中的xpm_cdc_gray.tcl的用途

背景 【Xilinx】vivado methodology检查中出现的critical Warning-CSDN博客 接上篇文章&#xff0c;在vivado进行 methodology检查时出现了严重警告&#xff0c;顺着指示查到如下一些问题 TIMING #1 Warning An asynchronous set_clock_groups or a set_false path (see con…...

windows中安装zookeeper

https://zhuanlan.zhihu.com/p/692451839 【zookeeper】在Windows上启动zookeeper_windows启动zk-CSDN博客 Index of /apache/zookeeper/zookeeper-3.9.2 Index of /apache/zookeeper/zookeeper-3.9.2 Zookeeper的应用场景 1、配置管理 2、服务注册中心 3、主从协调 4、…...

直接写和放在函数中不同的R语言用法

索引数据框中的某一列 df$A可以索引数据框df中列名为A的列的所有值。那么假如列名是一个R对象怎么做&#xff1f; df <- data.frame(A1:5, B(1:5)*2)df$A## [1] 1 2 3 4 5needed_column A# df$needed_column ? Wrong# 注意是双方括号 df[[needed_column]]## [1] 1 2 3 4…...