> 技术文档 > 【c++】c++的四种类型转换(static_cast,reinterpret_cast,const_cast,dynamic_cast)

【c++】c++的四种类型转换(static_cast,reinterpret_cast,const_cast,dynamic_cast)


小编个人主页详情<—请点击
小编个人gitee代码仓库<—请点击
c++系列专栏<—请点击
倘若命中无此运,孤身亦可登昆仑,送给屏幕面前的读者朋友们和小编自己!
在这里插入图片描述


目录

    • 前言
    • 一、c语言中的类型转换
      • 隐式类型转换
      • 强制类型转换
      • const常变量的强制类型转换
      • 总结
    • 二、c++的四种类型转换
      • static_cast
      • reinterpret_cast
      • const_cast
      • dynamic_cast
    • 三、RTTI
    • 总结

前言

【c++】特殊类的设计(不能拷贝的类,只能在堆/栈上创建对象的类,不能被继承的类,单例模式——饿汉模式、懒汉模式)——书接上文 详情请点击<——
本文由小编为大家介绍——【c++】c++的四种类型转换(static_cast,reinterpret_cast,const_cast,dynamic_cast)


一、c语言中的类型转换

在c语言中,如果赋值运算符左右两侧类型不匹配,形参和实参的类型不匹配,返回值类型和接收返回值的类型不匹配就会发生类型转换
c语言中共有两种类型转换:隐式类型转换和显示的强制类型转换

隐式类型转换

  1. 隐式类型转换:隐式类型转换会产生临时变量,编译器在编译阶段进行,如果转换失败,则编译失败,可以隐式转换极为相近的类型,比如浮点数和整形进行转换,对于单参数的构造函数也支持隐式类型转换,c++11中也支持了多参数的隐式类型转换
#include using namespace std;class A{public:A(int a):_a(a){}private:int _a;};class B{public:B(int b, int c):_b(b),_c(c){}private:int _b;int _c;};int main(){//隐式类型转换int a = 0;double b = a;//单参数的构造函数可以进行隐式类型转换A aa = 1;//c++11中多参数的构造函数也可以进行隐式类型转换B bb = { 2, 3 };return 0;}

运行结果如下,无报错成功
【c++】c++的四种类型转换(static_cast,reinterpret_cast,const_cast,dynamic_cast)

  1. 当然如果我们在类中不想要类型转换发生,可以使用explicit修饰对应成员函数即可限制类型转换
#include using namespace std;class A{public:explicit A(int a):_a(a){}private:int _a;};class B{public:explicit B(int b, int c):_b(b),_c(c){}private:int _b;int _c;};int main(){//隐式类型转换int a = 0;double b = a;//单参数的构造函数可以进行隐式类型转换A aa = 1;//c++11中多参数的构造函数也可以进行隐式类型转换B bb = { 2, 3 };return 0;}

运行结果如下
【c++】c++的四种类型转换(static_cast,reinterpret_cast,const_cast,dynamic_cast)

强制类型转换

  1. 显示的强制类型转换:需要用户使用 () 手动进行转换,强制类型转换近似关系的类型,例如指针和整形int之间,虽然指针和整形int是两种不同的类型,但是指针的本质是地址的编号,从小到大的地址进行编号,指针其实也是表示着数据的大小,整形int也是用于表示数据的大小,所以指针和整形int有近似关系,可以进行转换
int main(){//强制类型转换int a = 0;int* pa = nullptr;pa = (int*)a;return 0;}

运行结果如下
【c++】c++的四种类型转换(static_cast,reinterpret_cast,const_cast,dynamic_cast)

  1. 当然如果是完全没有关系的两种类型那必然是不可以使用强制类型转换的,例如:string和vector之间是完全不相同的两种类型就不可以使用强制类型转换进行转换
int main(){string str;vector<int> v;str = (string)v;return 0;}

运行结果如下,完全不同的类型无法进行强制类型转换
【c++】c++的四种类型转换(static_cast,reinterpret_cast,const_cast,dynamic_cast)

const常变量的强制类型转换

  1. 对于const修饰的变量称为常变量,它开了空间进行存储数据,由于被const进行修饰,所以变量具有常性,数据不可以进行修改,但是我们可以通过类型转换,通过地址解引用去修改它存储的数据的值,但是这种方式是不安全的,因为本质上我们使用const进行修饰变量,就是期望不进行修改数据
int main(){//const常变量const int c = 0;//这种转换方式是不安全的int* pc = (int*)&c;(*pc)++;cout << c << endl;cout << *pc << endl;return 0;}

运行结果如下
【c++】c++的四种类型转换(static_cast,reinterpret_cast,const_cast,dynamic_cast)

  1. 奇怪,小编不是取出常变量的地址,通过指针解引用的方式修改地址上存储的数据了吗?为什么这里的n和*pc的值却不一样,为什么n不是1?
  2. 那么接下来小编调试带领大家看一下监视窗口,监视窗口显示的是实际内存中存储的值

【c++】c++的四种类型转换(static_cast,reinterpret_cast,const_cast,dynamic_cast)

  1. 那么继续执行语句,结果如下,???什么?在实际的内存中c修改成1了

【c++】c++的四种类型转换(static_cast,reinterpret_cast,const_cast,dynamic_cast)

  1. 那么继续执行语句,打印一下c,这你扯不扯,编译器给c打印的不是内存中的实际值,而是c最初值0

【c++】c++的四种类型转换(static_cast,reinterpret_cast,const_cast,dynamic_cast)

  1. 其实原因就是编译器认为这种取出常变量的地址,通过指针解引用的方式修改常变量的方式是不安全的,所以编译器对于访问打印常变量采用的方式是直接使用最初值进行替换或者常变量初始化的时候除了初始化常变量,并且还会一并将初始值放到寄存器中,当需要使用常变量的时候去寄存器中取常变量的初始值,不同的编译器实现可能不同
  1. 那么我们知道了这里为什么编译器打印c的值是初始值了,如果小编非要编译器取出变量的值的时候每次去内存里去取,那么就可以使用volatile关键字进行修饰变量,被volatile修饰的变量编译器在取它的值的时候,每次都会去内存中去取
int main(){//const常变量volatile const int c = 0;//这种转换方式是不安全的int* pc = (int*)&c;(*pc)++;cout << c << endl;cout << *pc << endl;return 0;}

运行结果如下,编译器在取出常变量c的值的时候,去内存中去取c的值,而不是进行常变量初始值的替换或者去寄存器中去取常变量的初始值
【c++】c++的四种类型转换(static_cast,reinterpret_cast,const_cast,dynamic_cast)

总结

  1. c语言的转换很简单,但是仍然存在一些坑
  2. 隐式类型转换会出现精度缺失,程序如果不小心疏忽,会造成代码错误等
  3. 例如下面的函数fun本意是想要将形参的类型写成int,但是这里疏忽误写为了size_t,那么传参后,发生了我们未能察觉的隐式类型转换,while判断一直成立,如下代码就会陷入死循环
void fun(size_t n){while (n >= 0){cout << n << endl;n--;}}int main(){fun(5);return 0;}

运行结果如下
【c++】c++的四种类型转换(static_cast,reinterpret_cast,const_cast,dynamic_cast)

  1. 显示类型转换,const类型转换和普通的强制类型转换不易区分
  2. 那么c++提出了自己的转换风格,可以有效的处理这些问题。由于c++兼容c语言,所以对于c++的转换只是一个建议,原有的c语言的转换仍然可以使用

二、c++的四种类型转换

c++引入了四种强制类型准换:分别是static_cast,reinterpret_cast,const_cast,dynamic_cast
下面小编将对这四种强制类型转换进行讲解

static_cast

static_cast也叫做静态转换,编译器执行的任何隐式类型转换都可以使用它,但是不能将它用于两种不相关的类型进行转换

int main(){int a = 0;double b = static_cast<double>(a);return 0;}

运行结果如下,无误
【c++】c++的四种类型转换(static_cast,reinterpret_cast,const_cast,dynamic_cast)

reinterpret_cast

reinterpret_cast可以用于平替c语言中近似类型的强制类型转换,用于将一种类型强制转化为另一种近似关系的类型,但是reinterpret_cast不可以用于const类型的强制类型转换

int main(){int a = 0;int* pa = reinterpret_cast<int*>(a);return 0;}

运行结果如下
【c++】c++的四种类型转换(static_cast,reinterpret_cast,const_cast,dynamic_cast)

int main(){const int a = 0;int* pa = reinterpret_cast<int*>(&a);return 0;}

运行结果如下
【c++】c++的四种类型转换(static_cast,reinterpret_cast,const_cast,dynamic_cast)

const_cast

const_cast用于const类型的强制类型转换,用于去掉常变量的const属性

int main(){const int a = 0;int* pa = const_cast<int*>(&a);return 0;}

运行结果如下
【c++】c++的四种类型转换(static_cast,reinterpret_cast,const_cast,dynamic_cast)

dynamic_cast

dynamic_cast也叫做动态转换,用于将父类对象的指针或引用转换为子类对象的指针或引用

  1. dynamic_cast只能用于父类含有虚函数的类(父类和子类构成多态)
  2. dynamic_cast会先检查能否转换成功,如果不能转换成功,则返回nullptr
  1. 向上转换:子类的指针或引用转化为父类的指针或引用,这种转换也叫做切割,切片,由于赋值兼容规则,这种转换是天然支持的,不需要进行类型转换
  2. 向下转换:父类的指针或引用转化为子类的指针或引用(使用dynamic_cast进行转化是安全的)
  3. 我们知道父类的指针或引用是有可能指向子类的指针或引用的,此时进行向下转化无非就是将原来的子类的指针或引用转化给子类的指针或引用,这完全是可以的,没有问题的,所以此时转化就是安全的,那么dynamic_cast就会允许转化,会返回转化后的类型的指针或引用
  4. 我们知道父类的指针或引用也有可能指向父类的指针或引用,此时进行向下转化,就会将父类的指针或引用转化为子类的指针或引用,此时由于父类的指针或引用指向的对象中没有子类那一部分的成员函数,如果进行赋值除了将父类的指针或引用指向的父类对象的那一部分成员函数之外,也会一并将未知内存的内容也给给子类的指针或引用,这可就坑了,子类的指针或引用可以接收父类对象的那一部分这没问题,但是如果接收了未知内存的内容,那么进行访问,或进行操作会造成越界访问或者崩溃,所以dynamic_cast认为这是不安全的,所以就不会允许进行转换,会返回一个nullptr,用户接收nullptr之后可以进行判断是否转化成功
  5. 为什么dynamic_cast可以进行检查?父类中有虚函数,子类继承父类,父类和子类构造多态,那么此时父类和子类就会有自己独特的一张虚函数表,编译器会在虚函数表中做一些独属于父类和子类自己的标记,dynamic_cast进行检查的时候会去对应对象的虚函数表中去检查这个标记来判断是这个对象指向的究竟是父类对象还是子类对象,进而就可以进行检查了
  6. 我们传入父类的指针,看能否进行转换
class A{public:virtual void f(){}};class B : public A{};void fun(A* a){B* b = dynamic_cast<B*>(a);if (b == nullptr){cout << \"转换失败\" << endl;}else{cout << \"转换成功\" << endl;}}int main(){A a;fun(&a);return 0;}

运行结果如下
【c++】c++的四种类型转换(static_cast,reinterpret_cast,const_cast,dynamic_cast)

  1. 我们传入子类的指针,看能否进行转换
class A{public:virtual void f(){}};class B : public A{};void fun(A* a){B* b = dynamic_cast<B*>(a);if (b == nullptr){cout << \"转换失败\" << endl;}else{cout << \"转换成功\" << endl;}}int main(){B b;fun(&b);return 0;}

运行结果如下
【c++】c++的四种类型转换(static_cast,reinterpret_cast,const_cast,dynamic_cast)

三、RTTI

RTTI(Run-time Type identification)也叫做运行时类型检查
c++通过以下几种方式都是运行时类型检查去支持RTTI

  1. typeid,用于打印变量的类型
  2. decltype,用于识别类型,进行定义变量
  3. dynamic_cast,用于将父类的指针或引用转化为子类的指针或引用

总结

以上就是今天的博客内容啦,希望对读者朋友们有帮助
水滴石穿,坚持就是胜利,读者朋友们可以点个关注
点赞收藏加关注,找到小编不迷路!