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

【C++】类型转换

📝前言:

这篇文章我们来讲讲C++的类型转换

🎬个人简介:努力学习ing
📋个人专栏:C++学习笔记
🎀CSDN主页 愚润求学
🌄其他专栏:C语言入门基础,python入门基础,python刷题专栏,Linux


文章目录

  • 一,C语言类型转换
    • 1. 隐式类型转换
    • 2. 显式类型转换
  • 二,C++隐式类型转换(重点)
    • 1. 内置转自定义
    • 2. 自定义转内置
    • 3. 自定义转自定义
  • 三,C++显式类型转换(重点)
    • 1. 类型安全
    • 2. 4个显式强制类型转换运算符
      • 2.1 static_cast
      • 2.2 reinterpret_cast
      • 2.3 const_cast
      • 2.4 dynamic_cast
  • 四,RTTI

一,C语言类型转换

强制类型转换可以发生在转换有意义的地方。什么叫有意义?

  • 比如 intdouble,两个同为表示数据的大小。
  • 比如 指针(地址) 转 int,因为指针地址也是一个数字。但是不能指针转 double

类型转换发生的场景

  • 返回值接受类型和返回类型不相同
  • 形参与实参类型不相同
  • 表达式运算的项类型不相同
  • 赋值运算符,左右两边类型不相同
  • 等等…

1. 隐式类型转换

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

int a = 1;
double b = 2.1;
cout << a + b << endl; // 输出 3.1

在运算时,a被隐式类型转换成了double再进行运算

2. 显式类型转换

显式强制类型转化:需要用户自己去显式在变量前用括号指定要转换的类型

int* ptr1 = &a;
int c = (int)ptr1;
cout << c << endl; // 输出:514849252

二,C++隐式类型转换(重点)

C++支持C语言的类型转换,同时还支持了:自定义类型 → 内置类型、内置类型 → 自定义类型、自定义类型 → 自定义类型

1. 内置转自定义

内置类型转成自定义类型需要构造函数的支持。实际上是:

  1. 把内置类型当参数,调用构造函数创建临时对象
  2. 用临时对象拷贝构造
  3. (不过上面两步可能会被编译器直接优化成一次构造)
class Add
{
public:Add(int a):_a(a){ }Add(int a, int b):_a(a),_b(b){ }void Print(){cout << _a + _b << endl;}
private:int _a = 1;int _b = 1;
};int main()
{Add add1 = 2; // 调用第一个拷贝构造Add add2 = { 2,2 }; // 调用第二个拷贝构造add1.Print(); // 输出 3add2.Print(); // 输出 4return 0;
}

如果在构造函数之前加explicit 则代表:该构造函数不能被隐式类型转换时调用。
explicit Add(int a)时,Add add1 = 2;会报错,因为创建临时对象时,无法调构造。

2. 自定义转内置

自定义转内置,要求:自定义类型内要支持:operator <转换类型>()的重载
为什么不是 <转换类型> operator(),因为这玩意已经被仿函数占用了

class A
{
public:A(double a):_a(a){}A(double a, int b):_a(a),_b(b){}operator double(){return _a;}
private:double _a = 1;int _b = 1;
};int main()
{A a1 = 2.1;double c = a1;cout << c << endl; // 输出: 2.1return 0;
}

3. 自定义转自定义

自定义A转自定义B:要求B有一个接受A为形参的构造函数(和内置转自定义类似)

class A
{
public:A(double a):_a(a){}A(double a, int b):_a(a),_b(b){}double Geta(){return _a;}private:double _a = 1;int _b = 1;
};class B
{
public:B(A a1):_a(a1.Geta()){}void Print(){cout << _a + _b << endl;}
private:double _a = 1.1;int _b = 1;
};int main()
{//A a1 = 2.1;//double c = a1;//cout << c << endl; // 输出: 2.1A a1 = 3.1;B b1 = a1;b1.Print(); // 输出: 4.1return 0;
}

三,C++显式类型转换(重点)

1. 类型安全

类型安全指:编程语言在编译和运行时提供保护机制,避免非法的类型转换和操作。

C++并不是一门类型安全的语言,它允许我们类型转换,尽管我们可能有非法行为(这些非法行为即为不安全)
比如⼀个int*的指针强转成double*,然后用这个指针访问就会出现越界(指针的类型决定的是访问时一次"看"的空间

C++提出了4个显式的命名强制类型转换static_castreinterpret_castconst_castdynamic_cast,为了让类型转换相对而言更安全。(会检查转换是否合理)

2. 4个显式强制类型转换运算符

2.1 static_cast

static_cast

  • 检查时机:编译时
  • 使用场景:
    • 用于基本数据类型(如 int 转 double)、继承关系中的指针/引用转换(向上转换或向下转换,但是要确保安全性)
    • 不能用于无关类型(如 int* 转 double*)或移除 const 属性(把const变量转成非const不行)

示例:

double d = 3.14;
int i = static_cast<int>(d); // 基本类型转换Base* base = new Derived();
Derived* derived = static_cast<Derived*>(base); // 向下转换(需确保安全)
  • 因为基类指针本身就指向派生类,再转换成派生类,不会有越界问题
  • 但是如果基类指针原本指向的就是基类,直接转换成派生类,就可能“多看”(有访问越界问题)

2.2 reinterpret_cast

reinterpret_cast

  • 检查时机:编译时
  • 用途:用于无关类型之间的转换(完全信任用户自己的行为,但是有极大的安全隐患)
    • int*char*,用户后续的访问行为可能会发生错误,引发未定义行为

示例:

int* ip = new int(42);
char* cp = reinterpret_cast<char*>(ip); // 将 int* 转为 char*

2.3 const_cast

const_cast

  • 检查时机:编译时
  • 用途:添加或移除 constvolatile 属性(但是大多数是移除,因为添加可以隐式类型转换)

示例:

const int x = 10;
int* px = const_cast<int*>(&x); // 移除 const 属性
*px = 20; // 未定义行为(x 可能是常量存储区的值)volatile T* volatilePtr = ...;
T* nonVolatilePtr = const_cast<T*>(volatilePtr); // 移除 volatile

volatile就是用来确保每次获得变量值都去内存里面重新取,避免优化后直接从寄存器里面取,从而错过变量实际被修改了

2.4 dynamic_cast

dynamic_cast

  • 检查时机:运行时类型检查
  • 用途:用于继承体系中的安全向下转换(将基类指针或引用安全转换成派生类指针或引用)
    • 如果基类指针原本指向的就是派生类,则转换可以成功
    • 如果基类指针原本指向的指向基类,则转换失败(返回nullptr,抛出异常)
  • 要求:基类必须是多态类型(也就是基类中必须有虚函数)
    • (因为dynamic_cast是运行时通过虚表中(外)存储的type_info判断基类指针指向的是基类对象还是派生类对象)

示例:

class Base { public: virtual ~Base() {} };
class Derived : public Base {};Base* base = new Derived();
Derived* derived = dynamic_cast<Derived*>(base); // 安全向下转换
if (derived) { /* 成功 */ }

四,RTTI

  • RTTI:运行时类型识别,程序在运行的时候才确定需要用到的对象是什么类型的。
  • RTTI 的代表运算符有两个,typeiddynamic_cast

typeid

  • typeid主要用于返回表达式的类型
  • typeid(e)e可以是任意表达式或类型的名字,typeid(e)的返回值是type_infotype_info派生类对象的引用,type_info可以只支持比较等于和不等于,name成员函数可以返回C风格字符串表示对象类型名字(不同编译器下可能不同)。可以参见:typeinfo头文件】
  • e不属于类类型或者是⼀个不包含任何虚函数的类时,typeid返回的是运算
    对象的静态类型(此时是编译时确定)
  • e是定义了至少⼀个虚函数的类的左值时,typeid的返回结果直到运行时才会求得

示例(编译器为vs2022):

#include <typeinfo>int main() {int x = 42;// const std::type_info& ti = typeid(x); // typeid 返回的是 const std::type_info&std::cout << typeid(x).name() << std::endl;  // 输出: int (不同编译器的输出效果可能不同,比如可能拿 i 表示 int)return 0;
}

🌈我的分享也就到此结束啦🌈
要是我的分享也能对你的学习起到帮助,那简直是太酷啦!
若有不足,还请大家多多指正,我们一起学习交流!
📢公主,王子:点赞👍→收藏⭐→关注🔍
感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!

http://www.xdnf.cn/news/923059.html

相关文章:

  • 抖音怎么下载视频?抖音怎么无水印下载别人的视频
  • 蓝凌的流程引擎队列分发器
  • Python whl安装包简介与制作完全指南
  • 【优选算法】前缀和
  • Windows 下端口占用排查与释放全攻略
  • LeetCode-413. 等差数列划分
  • Go深入学习延迟语句
  • 【QT】输入类控件 详解
  • 嵌入式里的时间魔法:RTC 与 BKP 深度拆解
  • 数据通信基础
  • 迷宫问题(一)(C++版本)
  • @ExceptionHandler 默认无法拦截 Aspect(切面)中抛出的异常
  • centos7编译安装LNMP架构
  • docker安装RabbitMQ
  • 一些因子的解释
  • 人工智能--AI换脸
  • iview框架主题色的应用
  • WebWorker-----高频面试题(浏览器篇)
  • 【每天一道算法题】用JavaScript实现的字符串比较算法
  • 【云架构】
  • 后端下载限速(redis记录实时并发,bucket4j动态限速)
  • Java 常用 API 分类总结(算法竞赛考前速记篇)- 适用于算法竞赛(如 CCF CSP、蓝桥杯、NOI)
  • 【PhysUnits】15.17 比例因子模块 (ratio.rs)
  • 河南建筑安全员B证考试最新精选题
  • Python 函数全攻略:函数基础
  • JavaSec-SpringBoot框架
  • JAVA理论第三章-多线程
  • Python实例题:Python计算微积分
  • 2025年06月07日Github流行趋势
  • go语言学习 第9章:映射(Map)