news 2026/4/23 15:02:55

【C++】--- 类型转换

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【C++】--- 类型转换

Welcome to 9ilk's Code World

(๑•́ ₃ •̀๑)个人主页:9ilk

(๑•́ ₃ •̀๑)文章专栏: C++


本篇博客主要是对C/C++中类型转换的梳理总结。

内置类型转换

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

1. 隐式类型转换:编译器在编译阶段自动进行,能转就转,不能转就编译失败,通常是在整形之间/整形和浮点数之间。

2. 显示类型转换:需要用户自己处理,通常是在指针和整形、指针之间发生。

//隐式类型转换 整形家族 整形与浮点型 size_t t = 21; double d = 21; printf("%d,%f\n", t, d); //强制类型转换 不同类型的指针之间 指针与整形 double* pd = &d; int* pi = (int*)pd; int o = (int)pi;

我们可以看到,对于这两种类型转换,转换的可视性比较差,所有的转换形式都是以一种相同形式书写,难以跟踪错误的转换。

内置类型转换为自定义类型

内置类型转换为自定义类型,需要在自定义类型提供对应的构造函数,这样才能进行单参和多参的隐式类型转换,此时会产生临时对象,再用这个临时对象构造(编译器不优化时):

class A { public: A(int a) :_a1(a) , _a2(a) { cout << "A(int a)" << endl; } A(int a1, int a2) :_a1(a1), _a2(a2) { cout << "A(int a1, int a2)" << endl; } A(const A&) { cout << "A(const A&)" << endl; } private: int _a1 = 1; int _a2 = 1; }; int main() { //单参和多参的隐式类型转换 A aa1 = 1; A aa2 = {1,2}; const A& aa3 = {2,2};//隐式类型产生临时对象 return 0; }

自定义类型转为内置类型

自定义类型可以通过内置类型转换为内置类型,需要注意的是operator 类型(类型转换符)实现,是没有返回类型的:

class B { public: B() {} operator int() { return _a1 + _a2; } private: int _a1 = 1; int _a2 = 1; }; int main() { B b1; int x = b1.operator int(); int y = b1; return 0; }

我们可以看到语法层面不管你是否显示调用operator int,底层都是转换成函数调用:

对于y这种其实是隐式类型转换的写法,我们可以用explicit关键字验证下,它能让禁止编译器禁止隐式类型转换:

自定义类型之间的转换

类型转换之间需要关联性,之前的显示和隐式就是关联性强弱的问题,这里自定义类型之间的转换也一样,需要对应构造函数支持:

class A { public: A(int a) :_a1(a) , _a2(a) { cout << "A(int a)" << endl; } A(int a1, int a2) :_a1(a1), _a2(a2) { cout << "A(int a1, int a2)" << endl; } A(const A&) { cout << "A(const A&)" << endl; } int get() const { return _a1 + _a2; } private: int _a1 = 1; int _a2 = 1; }; class B { public: B() {} B(const A& aa) :_a1(aa.get()) { cout << "B(const A& aa)" << endl; } explicit operator int() { return _a1 + _a2; } private: int _a1 = 1; int _a2 = 1; }; int main() { A aa1(1); B bb1 = aa1; const B& ref = aa1; return 0; }

下面我们看这样的一个场景:

typedef _list_iterator<T,T&,T*> iterator; typedef _list_iterator<T,const T&,const T*> const_iterator; zlist::list<int> it = {1,2,3,4}; zlist::list<int>::const_iterator cit = it.begin();

这里将一个普通迭代器赋值给const迭代器类型,这里会报错,但是注意报错原因不是权限缩小,因为只有指针引用采用权限问题,这里的原因是两种迭代器是同一个类模板参数传不同的模板参数产生的不同类型,因此这里it对应的是普通迭代器,与const迭代器类型不匹配,也就是说,这里其实是个类型转换问题。

此时就可以函数来实现自定义类型之间的转换,我们需求是普通迭代器转换成const迭代器,因此我们写个对应的构造函数即可,当传参传的是普通迭代器时,这里相当于是个普通构造函数:

ListIterator(const ListIterator<T, T&, T*>& it) :_node(it._node) { cout << "ListIterator(const ListIterator<T, T&, T*>& it)" << endl; }

C++中的类型转换

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

static_cast

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

int a = 20; double d = static_cast<int>(a); //对标隐式类型转换

reinterpret_cast

reinterpret_cast操作符通常为操作数的位模式提供较低层次的重新解释,用于将一种类型转换为另一种不同的类型,用于两个不相关类型之间的转换:

int* pa = &a; double* pd = reinterpret_cast<double*>(pa);//对标显示类型转换

const_cast

const_cast最常用的用途就是删除变量的const属性,方便赋值,它对应的是强制类型转换中有风险的去掉const属性:

const int a = 2; int* p = (int*)&a; int* p2 = const_cast<int*>(&a); *p = 3;

我们打印看下结果:

cout << a << endl; //2 cout << *p2 << endl; //3

由于a本身是const int,因此在编译期就把它优化为一个立即数2,不再去内存里取值,此时我们要想它读的是最新的,可以加上volatile关键字修饰,告诉编译器每次从内存中读取最新的:

volatile const int a = 2; int* p = (int*)&a; int* p2 = const_cast<int*>(&a);

dynamic_cast

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

  • 向上转型:子类对象指针/引用->父类指针/引用(不需要转换,赋值兼容规则)
  • 向下转型:父类对象指针/引用->子类指针/引用(用dynamic_cast转型是安全的),毕竟子类比父类多一部分成员的话,你访问会造成越界

注意:

1. dynamic_cast只能用于父类含有虚函数的类,因为运行时类型检测需要运行时的类型信息,而这个信息是存储在虚函数表中的,只有定义了虚函数的类才有虚函数表。

2. dynamic_cast会先检查是否能转换成功,能成功(pc指向子类对象)则转换,不成功(pc指向父类对象)则返回NULL

class A { public: virtual void f() {} int _a = 1; }; class B : public A { public: int _b = 2; }; void fun(A* pa) { // dynamic_cast会先检查是否能转换成功(指向子类对象),能成功则转换, // (指向父类对象)不能则返回NULL B* pb1 = dynamic_cast<B*>(pa); if (pb1) { cout << "pb1:" << pb1 << endl; cout << pb1->_a << endl; cout << pb1->_b << endl; pb1->_a++; pb1->_b++; cout << pb1->_a << endl; cout << pb1->_b << endl; } else { cout << "转换失败" << endl; } } int main() { A a; B b; fun(&a); fun(&b); return 0; }

注意:

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

RTTI

RTTI:Run-time Type identification的简称,即:运行时类型识别。

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

1. typeid运算符

2. dynamic_cast运算符

3. decltype

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

DeepSeek-V3.1震撼发布:混合推理架构引领AI交互新纪元

DeepSeek-V3.1震撼发布&#xff1a;混合推理架构引领AI交互新纪元 【免费下载链接】DeepSeek-V3.1-Base DeepSeek-V3.1 是一款支持思考模式与非思考模式的混合模型 项目地址: https://ai.gitcode.com/hf_mirrors/deepseek-ai/DeepSeek-V3.1-Base 2025年8月21日下午&…

作者头像 李华
网站建设 2026/4/23 10:47:35

【北理工-AAAI26】MODA:首个无人机多光谱目标检测数据集

文章&#xff1a;MODA: The First Challenging Benchmark for Multispectral Object Detection in Aerial Images代码&#xff1a;https://github.com/shuaihao-han/MODA单位&#xff1a;北京理工大学一、问题背景&#xff1a;航拍检测难在哪&#xff1f;多光谱潜力为何难释放&…

作者头像 李华
网站建设 2026/4/23 10:44:01

用 Melody 听遍全网音乐!加上cpolar这个工具,随时随地享受专属歌单

文章目录前言1. 添加镜像源2. 本地部署Melody3. 本地访问与使用演示4. 安装内网穿透5. 配置Melody公网地址6. 配置固定公网地址前言 Melody 的主要功能是整合多个平台的音乐资源&#xff0c;用户可以搜索、播放、下载歌曲&#xff0c;还能将喜欢的音乐上传到云盘&#xff0c;方…

作者头像 李华
网站建设 2026/4/23 13:35:57

一文盘点家政有哪些类型

居民生活品质不断提升&#xff0c;家政服务已经从简单的日常保洁&#xff0c;扩展到清洁维护、育婴照护、居家维修等一整套生活支持体系。为了帮助家庭更高效地选择家政服务&#xff0c;本篇从“服务类型”“经营模式&#xff08;直营 / 非直营&#xff09;”“行业代表品牌”三…

作者头像 李华
网站建设 2026/4/23 10:48:55

Wan2.2-T2V-A14B如何生成带有弹幕评论的模拟播放界面?

Wan2.2-T2V-A14B如何生成带有弹幕评论的模拟播放界面&#xff1f; 在短视频内容爆炸式增长的今天&#xff0c;用户早已不满足于“看视频”这一单向行为——他们更渴望参与、互动与共鸣。尤其是在B站、抖音等平台上&#xff0c;一条视频是否“火”&#xff0c;往往不是由画质或剧…

作者头像 李华
网站建设 2026/4/23 10:48:44

从零上手MiniCPM 2.0:端侧大模型本地部署全攻略

模型概述&#xff1a;重新定义轻量级AI的性能边界 【免费下载链接】MiniCPM-V-2 项目地址: https://ai.gitcode.com/OpenBMB/MiniCPM-V-2 在大语言模型参数竞赛愈演愈烈的当下&#xff0c;面壁智能与清华大学自然语言处理实验室联合研发的MiniCPM系列&#xff0c;正以&…

作者头像 李华