【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语言类型转换
强制类型转换可以发生在转换有意义的地方。什么叫有意义?
- 比如
int
转double
,两个同为表示数据的大小。 - 比如 指针(地址) 转
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. 内置转自定义
内置类型转成自定义类型需要构造函数的支持。实际上是:
- 把内置类型当参数,调用构造函数创建临时对象
- 用临时对象拷贝构造
- (不过上面两步可能会被编译器直接优化成一次构造)
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_cast
、reinterpret_cast
、const_cast
、dynamic_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
- 检查时机:编译时
- 用途:添加或移除
const
或volatile
属性(但是大多数是移除,因为添加可以隐式类型转换)
示例:
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 的代表运算符有两个,
typeid
和dynamic_cast
typeid
typeid
主要用于返回表达式的类型typeid(e)
中e
可以是任意表达式或类型的名字,typeid(e)
的返回值是type_info
或type_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;
}
🌈我的分享也就到此结束啦🌈
要是我的分享也能对你的学习起到帮助,那简直是太酷啦!
若有不足,还请大家多多指正,我们一起学习交流!
📢公主,王子:点赞👍→收藏⭐→关注🔍
感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!