【C++】告别“类型转换”踩坑,从基础到四种核心强制转换方式
各位大佬好,我是落羽!一个坚持不断学习进步的学生。
如果您觉得我的文章还不错,欢迎多多互三分享交流,一起学习进步!
也欢迎关注我的blog主页: 落羽的落羽
文章目录
- 一、回顾C语言的类型转换
- 二、C++中的类型转换
- 1. 内置类型转为自定义类型
- 2. 自定义类型转为内置类型
- 3. 自定义类型之间的转换
- 4. 类型安全与C++的四种强制类型转换方式
- 4.1 什么是类型安全?
- 4.2 static_cast
- 4.3 reinterpret_cast
- 4.4 const_cast
- 4.5 dynamic_cast
一、回顾C语言的类型转换
C语言的类型转换主要是隐式类型转换和强制类型转换:
- 隐式类型转换,是编译器在特定情况下自动进行的类型转换,通常发生在不同类型的表达式运算中。主要是整型之间、整型与浮点型之间、浮点型之间的转换。
int a = 1;
double b = 2.234;
a = b;
- 强制类型转换:是我们通过显式语法手动指定的类型转换,主要是整型和指针、指针和指针之间的转换。
int a = 1;
char* p = (char*)a;
int* p1 = (int*)malloc(sizeof(int));
但是,并非所有类型直接都能转换。例如double就无法转为指针类型。
二、C++中的类型转换
1. 内置类型转为自定义类型
随着自定义类型的广泛使用,C++也支持了内置类型和自定义类型之间的相互转换:
- 内置类型转换为自定义类型,需要自定义类型的相关构造函数支持。非常好理解,用内置类型转换为自定义对象,就相当于构造一个自定义对象。
class A
{
public:A(int a):a1(a),a2(a){ }private:int a1;int a2;
};
而当构造函数前加上关键字explicit
,可以使这个构造函数不支持隐式类型转换。这就是之前我们学习智能指针时构造函数加了它的原因,我们不希望一个普通指针悄悄转换为智能指针。不过,还是支持强制类型转换的:
2. 自定义类型转为内置类型
一个自定义类型对象想要转换为内置类型,需要内部重载operator()
。这里不是常规的运算符重载,和仿函数中重载operator()
的写法也不一样。它的用法是operator 类型(){...}
(也可以加explicit修饰),转换的方式是函数体内自定义的,举个例子:
class A
{
public:A(int a):a1(a), a2(a){}operator int(){return a1 + a2;}private:int a1;int a2;
};
智能指针中,关于向bool类型的转换,就是这样写的:
3. 自定义类型之间的转换
自定义类型之间的转换,也是需要有相关的构造函数,很好理解,不再赘述。
4. 类型安全与C++的四种强制类型转换方式
4.1 什么是类型安全?
类型安全是指,编程语言在编译和运行时提供保护机制,避免非法的类型转换和操作,导致出现内存访问错误等,从而减少程序运行时的错误。
C语言不是类型安全的语言, 因为C语言允许隐式类型转换,一些特殊情况下就会导致越界访问的内存错误。不合理的使用强制类型转换也会导致问题,比如一个int*
的指针强转成 double*
的话,访问就会出现越界。
C++兼容C语言,也支持隐式类型转换和强制类型转换,它也不是类型安全的语言。因此,C++提出了四个显示强制类型转换 操作符static_cast / reinterpret_cast / const_cast / dynamic_cast
,就是为了让类型转换相对而言更安全。
4.2 static_cast
static_cast
用于两个意义相近的类型之间的转换,比如整型之间、整型和浮点型之间、普通类型和其引用类型之间等。
double a = 1.234;
int b = static_cast<int>(a);
float c = static_cast<float>(b);int&& d = static_cast<int&&>(b);
4.3 reinterpret_cast
reinterpret_cast
用于底层的类型转换,比如将一种类型的指针/引用直接转换为另一种类型。这种类型的转换后,对于内存的访问方式就会改变了,因此使用时需要很谨慎,自己要清楚明白使用后是否会出现问题。
int a = 1;
int* p1 = &a;
char* p2 = reinterpret_cast<char*>(p1);
4.4 const_cast
const_cast
用于const对象向非const对象的转换,会去掉const属性,使用时同样需要谨慎。
4.5 dynamic_cast
dynamic_cast
主要用于多态类型之间的转换,尤其是用于基类指针/引用向派生类指针/引用的转换,因为派生类的指针/引用向基类指针/引用的转换是天然支持的(也就是切片)。
但是,它不是简单基类转换为派生类。一个基类的指针/引用,它可能指向的是一个基类的对象,也可能指向的是一个派生类对象的切片。而dynamic_cast
只能作用在后者,只有这个基类指针/引用指向的是派生类对象的切片,才能完成转换。对于前者情况,就不能转换,如果是基类指针指向基类,转换失败返回nullptr,如果是基类引用指向基类,转换失败抛出bad_cast异常。
dynamic_cast
还要求基类必须是多态类型,也就是必须有虚函数,因为它的运行时通过虚表中存储的type_info来判断一个基类指针/引用指向的是基类对象还是派生类对象。
class A
{
public:virtual void func() {}
};class B : public A
{
};
本篇完,感谢阅读