news 2026/1/11 12:56:05

C++11(五):类型转换全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++11(五):类型转换全解析

目录

c++的类型转换

1. C语言中的类型转换

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

3. c++强制类型转换解析

3.1 static_cast

3.2 reinterpret_cast

3.3 const_cast

3.4 dynamic_cast

4. RTTI


c++的类型转换

c语言和c++是一个强类型的语言,也就是变量和变量之间有明显的类型的区分

1. C语言中的类型转换

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

1. 隐式类型转化:编译器在编译阶段自动进行,能转就转,不能转就编译失败

2. 显式类型转化:需要用户自己处理

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

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

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

static_castreinterpret_castconst_castdynamic_cast

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

1. 隐式类型转化有些情况下可能会出问题:比如数据精度丢失

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

因此C++提出了自己的类型转化风格,注意因为C++要兼容C语言,所以C++中还可以使用C语言的转化风格

c++兼容c语言留下来的隐式类型转换和显示转换,但是c++觉得c语言做的不规范,c++想规范一下,标准c++为了加强类型的可视性,引入了四种命名的强制类型转换操作符

3. c++强制类型转换解析

3.1 static_cast

对于c语言中的隐式类型转换,相近的类型就使用这个

int i = 1; double d = 8.88; i = static_cast<int>(d); cout << i << endl;

3.2 reinterpret_cast

对于c语言中的强制类型转换,不相近的类型就用这个

int i = 1; double d = 8.88; i = static_cast<int>(d); cout << i << endl; int* p = nullptr; p = reinterpret_cast<int*>(i); cout << p << endl;

总结:如果你不相近的类型使用了static_cast就不可以,如果相近的类型使用了reinterpret_cast也不可以,总之c++是为了实现你一看用哪个cast就知道,但注意你并没有改变原来变量的类型,也就是d还是8.88,还是double类型

3.3 const_cast

const_cast就是去除const属性的

const int ci = 10; int* pi = const_cast<int*>(&ci); *pi = 20; cout << *pi << endl; cout << ci << endl;

这种情况是未定义行为:

原变量是const修饰的,但是经你这么修改,原变量本来是const,但是可以通过指针来修改,这种是不合法的,结果是未定义的

// 可能的结果: // 1. 程序崩溃; // 2. 输出 ci 仍为 10(编译器优化,a 被存储在只读内存/寄存器); // 3. 输出 ci为 20(极少数未优化场景)。

我的编译器优化之后ci仍然为10,原因

可以看到通过调试已经修改了变量的值,但是为什么打印ci是10

这是是去寄存器当中找的,就是因为你原来是const对象,它认为你是不可能修改的,所以放到寄存器当中,这样速度快,但是你这里修改的是内存当中的值(ci当中内存是变20了),然后打印的时候由于ci寄存器有直接就拿了,本质是由于编译器对const对象存取优化机制导致的,甚至有些直接优化成常量10了

为了避免编译器优化,可以通过加volatile关键字,让编译器每次都到内存当中取

volatile const int ci=10;

但是一般不要这么编写,本来好好的const常量被你修改了之后能变

一般都是const指针或者const引用去除const属性(只有这种场景是合法的)

3.4 dynamic_cast

用于多态类型的转换

c++中子类对象可以赋值给父类的对象、指针、引用、这个过程是语法天然支持的,可以成功

https://blog.csdn.net/Laydya/article/details/148145002

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

向上转型:子类对象指针/引用->父类指针/引用(不需要转换,赋值兼容规则) 向下转型:父类对象指针/引用->子类指针/引用(用dynamic_cast转型是安全的)

注意: 1. dynamic_cast只能用于含有虚函数的类 2. dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0

class A { public: virtual void f() {} int _a; }; class B :public A { public: int _b; }; void f_cast(A* pa) { //如果想要区分pa是指向父类,还是子类对象? B* pb = (B*)pa; pb->_a = 1; pb->_b = 2; } int main() { A a; B b; A* pa = &a; f_cast(pa); B* pb = &b; f_cast(pb); return 0; }

这里强制类型转换了之后,其实就是这个pb指针能看到的东西更多了,也就是指针的意义就是根据类型能够看到多大的空间,由于你原来是A的类型,但是你这里强制类型转换成b了,所以看到的东西变多了,然后你pb-> _b 就会越界访问

如果你本身就是一个子类对象,那就没问题,因为你本身就可以看到这么多东西

class A { public: virtual void f() {} int _a; }; class B :public A { public: int _b; }; void f_cast(A* pa) { //如果想要区分pa是指向父类,还是子类对象? B* pb = dynamic_cast<B*>(pa); if (pb != nullptr) { cout << "转换成功" << endl; pb->_a = 1; pb->_b = 2; } else { cout << "转换失败" << endl; } } int main() { A a; B b; A* pa = &a; f_cast(pa); B* pb = &b; f_cast(pb); return 0; }

使用dynamic_cast,如果pa是指向子类对象,则转换成功

反之如果是父类对象,则转换失败

注意:只能针对继承中的多态类型(父类必须包含虚函数)如果你把A里面的虚函数去掉那就编译不过去了

原理:dynamic_cast通过去虚表的上方存储的标识信息,来判断指向父类对象还是子类对象,如果没有虚函数,则没有虚表,就无法判断了

总结:尽量少用类型转换,如果要用,就用规范一些,让别人一看到就知道你在干嘛

4. RTTI

RTTI(Run-Time Type Information,运行时类型信息)是 C++ 提供的在程序运行阶段获取对象 / 指针实际类型信息的机制,核心作用是打破编译期的类型绑定,让程序在运行时识别多态类型(比如基类指针指向的派生类对象的真实类型)。

C++ 标准中,RTTI 的支持是可选的(但主流编译器如 GCC/Clang/MSVC 均默认开启),且仅对带有虚函数的类(多态类)能正确返回动态类型;非多态类的 RTTI 仅能返回编译期类型。

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

1.typeid运算符

typeid运算符返回std::type_info对象,包含类型的名称、哈希值等信息,可用于:

  • 比较两个对象 / 指针的实际类型是否相同;

  • 获取类型的名称(注意:名称格式由编译器决定,不一定是类的原名)。

  • 只有多态才能返回指向的类型,如果没有多态返回的是编译期类型(基类),而非实际指向的派生类

2.dynamic_cast运算符

这个上面已经做了详细的讲解,也就是通过运行时的类型确定的,运行起来后通过查虚表,虚表里面有标识来辨别实际类型,仅仅针对多态

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!