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

C++进阶(10)C++的类型转换

1.C语言中的类型转换

1.1C语言中的两种类型转换

在C语言中,以下场景:

  • 赋值运算符左右两侧类型不同;
  • 形参与实参类型不匹配;
  • 返回值类型与接收返回值类型不一致;

就需要进行类型转换。


C语言中总共有两种形式的类型转换:隐式类型转换、显式类型转换。

  • 隐式类型转换:编译器在编译阶段自动进行,能转就转,不能转就编译失败。
  • 显式类型转换:需要用户自己处理。
void Test () { int i = 1; // 隐式类型转换 double d = i; printf("%d, %.2f\n" , i, d); int* p = &i; // 显示的强制类型转换 int address = (int) p; printf("%x, %d\n" , p, address); }

1.2缺陷

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


1.3C++中的常见的类型转换

C语言中的类型转换不是任意类型直接都可以,需要两个类型之间有一定的关联。

  • 【①】内置类型之间。

    • 隐式类型转换:整形之间、整形和浮点数之间。(强关联的类型)

    • 显式类型转换:指针和整形、指针之间。(弱关联的类型)

  • 【②】内置类型和自定义类型之间。

  • 【③】自定义类型和自定义类型之间。

隐式类型转换的类型,都是表示数据大小的类型。

显式类型转换的类型,在于:虚拟进程地址空间是以字节为单位对内存进行标号,指针表示地址,就是那些一个字节一个字节的编号,空指针代表第0个字节的地址。编号由小到大,也可以表示大小,整型也是表示大小,所以可以强制转换。

指针和整型之间只能强制类型转换。


【②】内置类型和自定义类型之间

  • 自定义类型 = 内置类型(不能直接转,依靠构造函数支持)
  • 内置类型 = 自定义类型(通过operator+类型)

实现一个A类型,来观察。

中间构造产生出来的是临时对象,如果要引用,需要加const。

如果不想依靠构造函数的隐式类型转换发生,可以加上explicit。

但是依靠构造函数的强制类型转换还是可以的。


自定义类型给内置类型赋值,通过“重载类型”,实现类型转换。

理论上来说应该是operator(),因为强制类型转换的运算符是(),但是operator()被仿函数占用了,因为函数调用的参数列表也是()。

所以这里的写法比较特别:

本质等价于下面的写法,就是调用operator int()。

语法层面有不同的写法,底层都是转换成对应的函数调用。

加上explicit表示不允许隐式类型转换。


智能指针的operator bool就是自定义类型转换成对应的布尔值。

应用场景就是使用智能指针进行逻辑判断。

还是模拟指针的行为,指针也可以直接进行逻辑判断。


【③】自定义类型之间(对应的构造函数支持)

// c、自定义类型和自定义类型之间 -- 对应的构造函数支持 class A { public: A(int a) :_a1(a) , _a2(a) {} A(int a1, int a2) :_a1(a1) , _a2(a2) {} private: int _a1 = 1; int _a2 = 1; }; class B { public: B(int b) :_b1(b) {} private: int _b1 = 1; }; int main() { A aa1(1); B bb1(2); bb1 = aa1; //报错 return 0; }

强制类型转换也不行。

两个类型之间要转换,一定要有一定的关联。

给B一个使用A类型参数的构造就可以支持了。

但是A类的成员是私有,所以需要提供访问接口。(或者友元类——破坏封装,不建议用)

bb1 = aa1就可以先用aa1构造一个bb1类型的临时对象,再调用赋值重载。

父类引用派生类的切片,不是类型转换。


自定义类型之间的转换的例子:

代码编译不过。

  • 权限的缩小?权限缩小和放大,仅限于const的指针和引用
  • 不是权限缩小,这里类型转换

普通迭代器和const迭代器是两个不同的类型:

所以这里ListIterator这个类模版需要提供由这个类模版实例化出的两个不同类之间的构造。


先来看一下标准库是否支持这样的语法:

显然标准库是支持这样的语法的。

参数始终是一个普通迭代器。

  • 在List类里面把__list_iterator这个类用T&、T*实例化成普通迭代器,那这个函数就是拷贝构造。(没有意义,编译器默认生成的拷贝构造就能完成内置类型的值拷贝)
  • 在List类里面把__list_iterator这个类用const T&、const T*实例化const迭代器,那这个函数就是构造。(用普通迭代器去构造const迭代器)

自己实现的List_Iterator也可以写一个类似的构造函数。

这里就是自定义类型之间的类型转换。


【类型转换一定要有关联的两个类型】

  • 内置类型之间:语言原生确定的关联;
  • 涉及自定义类型:通过函数去建立对应的关联;

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

C风格的转换格式很简单,但是有不少缺点的:

  • 隐式类型转化有些情况下可能会出问题。比如:数据精度丢失。
  • 显式类型转换将所有情况混合在一起,代码不够清晰。

因此C++提出了自己的类型转化风格。


【注意】因为C++要兼容C语言,所以C++中还可以使用C语言的转化风格

3.C++强制类型转换

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

  • static_cast(静态转换)
  • reinterpret_cast(重新解释转换)
  • const_cast(常转换)
  • dynamic_cast(动态转换)

3.1static_cast

【static_cast】

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

C++希望规范隐式类型转换(静态转换),于是给出了static_cast。

表明这是一个隐式类型转换——可能会导致:精度丢失、截断、提升……等问题。

3.2reinterpret_cast

reinterpret:重新解释。

【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和int*有一定的关联,但是关联不是很紧密 int* p = reinterpret_cast<int*>(a); return 0; }

走隐式类型转换的地方用不了reinterpret_cast。

走强制类型转换的地方用不了static_cast。

3.3const_cast

【const_cast】

  • 最常用的用途就是删除变量的const属性,方便赋值。
void Test() { const int a = 2; int* p = const_cast<int*>(&a); *p = 3; cout << a << endl; }

这里使用C语言的强制类型转换也是可以的。

之所以用const_cast,就是警示你这里的转换是有风险的地方。


这种情况下,用p2去修改b是未定义行为。

原因是编译器的优化,编译器面对const变量,有些地方会放到寄存器,有些地方不会,直接把b替换成2(类似于宏)。

VS下面就是直接把b替换成2,*p确实改了b的内存,但是打印的地方的b是直接替换成2。

从监视窗口也能看到b是修改成3了的:

打印出来却是2,就是因为b被替换成了常量。


这里可以考虑加上volatile关键字,表示取b的值不会直接用常量替换,会到内存中去取。

从汇编也能看到:






同一个内存地址,读出不同的值!矛盾


3.4dynamic_cast

【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会先检查是否能转换成功,能成功则转换,不能则返回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; }

父类指针(引用)可以转换成子类指针(引用),static_cast和reinterpret_cast都可以。

但是它们都被认为是不安全的。


不能隐式类型转换。

可以强制类型转换,但都是不安全的。

指向父类的指针,转换成指向子类的指针时,是有风险的——后续访问存在越界访问的风险。

指向子类的指针,转换成指向父类的指针时,是安全——只需要看子类中存储父类成员的那部分。


越界读:

越界写:


使用dynamic_cast才是安全的:

这个转换要求多态类型——父类有虚函数。

【结论】dynamic_cast相当于是能够识别一个父类的指针,到底是真的指向一个父类对象,还是指向一个子类对象中的父类成分。(通过转换成子类指针是否成功的方式)


【注意】

  • 强制类型转换关闭或挂起了正常的类型检查,每次使用强制类型转换前,程序员应该仔细考虑是否还有其他不同的方法达到同一目的,如果非强制类型转换不可,则应限制强制转换值的作用域,以减少发生错误的机会。

【强烈建议】避免使用强制类型转换。

4.RTTI(了解)

RTTI(Run-time Type identification):运行时类型识别。

C++通过以下方式来支持RTTI:

  • typeid运算符(获取对象的类型字符串,也是在编译时就生成的)
  • dynamic_cast运算符(编译时就去识别一个指针是父类还是子类)
  • decltype(严格来说是编译时推出这个类型的,但是从语义上来说是运行时推出对象类型作参数)

5.常见面试题

1. C++中的4中类型转化分别是:____________________________________

2. 说说4中类型转化的应用场景。

http://www.jsqmd.com/news/637527/

相关文章:

  • 终极React Server Components Demo架构揭秘:客户端与服务端组件的完美协作指南
  • 革命性监控工具ebpf_exporter:深度解析内核性能的终极指南
  • 2026年口碑好的1688店铺托管外包/宁波1688店铺托管综合评价公司 - 品牌宣传支持者
  • 2026年知名的广东储罐大件运输优选公司推荐 - 品牌宣传支持者
  • 斯坦福首门AI开发课程:人机协作工程而非氛围编程
  • 如何快速掌握WTM多UI框架实战:LayUI、React、VUE、Blazor全解析
  • SlateDB范围查询优化技巧:实现高效数据扫描的5个关键策略
  • 终极指南:DefectDojo与其他安全工具对比,为什么它是你的最佳漏洞管理选择
  • AppleRa1n完整指南:iOS 15-16设备iCloud激活锁绕过终极方案
  • 5分钟掌握sakura.css暗色模式:打造现代网站的终极视觉体验
  • iOS YYKline核心组件解析:Model、Painter与Config架构设计
  • 【MySQL】表基础:CRUD操作
  • 2026年目前推荐机床钣金防护企业哪个好,风琴防护罩/排屑机/机床拖链/机床钣金防护,机床钣金防护直销厂家推荐分析 - 品牌推荐师
  • 【12.MyBatis源码剖析与架构实战】12.2 动态标签解析过程-系统初始化时
  • Nacos 生产级安全实践:精细化鉴权、灰度平滑过渡与全量操作
  • Transformer实战(39)——多模态生成式Transformer
  • 2026年制造业1688托管运营装修品牌公司推荐 - 行业平台推荐
  • VSCode PlantUML终极指南:如何快速绘制专业UML图表
  • SopCastComponent实战案例:构建你的第一个Android直播应用
  • ESP32-CAM的SD卡能跑多快?实测SDMMC 4线模式下的文件读写性能与优化
  • 华硕A豆14 I421E 原厂Win10 20H2系统 分享下载
  • moonlight-android入门指南:如何在5分钟内将PC游戏串流到Android设备
  • CloudCompare标注自己的点云数据(部件分割)
  • 终极Jellyfin Media Player Qt WebEngine优化指南:10个提升播放性能的实用技巧
  • Rocket.Chat移动端终极优化指南:打造完美响应式聊天体验
  • C语言stdio.h指南:从printf到文件操作的全方位解析
  • 别再复制粘贴了!手把手教你为STM32F103ZE手动移植FreeRTOS v202212.01(附完整源码包)
  • 专知智库白皮书(一):什么是余行税?企业隐形生存税的定义与本质
  • 新手小白学习人工智能,推荐哪些入门书籍和课程?适合零基础的有哪些?|2024新手必看
  • explainerdashboard核心组件详解:从SHAP值到特征重要性分析