[C++ 从入门到精通] 6.static_cast、dynamic_cast等显示类型转换

简介: [C++ 从入门到精通] 6.static_cast、dynamic_cast等显示类型转换

一. 隐式类型转换

含义:隐式类型转换:系统自动进行,不需要开发人员介入。

int m = 3 + 45.6;      //48

因为返回的int型,所以系统自动去除掉小数点后面的值,结果为48,这种属于隐式转换类型。


二. 显式类型转换(强制类型转换)

1、C语言的强制类型转换风格:

//int k = 5 % 3.2;     //语法错误:%非法,右操作数包含“double”类型
int k = 5 % int(3.2);  //将3.2强制转换成int型3,C语言风格的强制类型转换,结果:余2

上面这种C语言的强制类型转换风格,没有类型检查,往整型上硬转,转的对不对需要程序员提供保障,例如强制将字符串类型转换成int类型:(int)"asa"就不行了。

2、针对上面的情况,C++提供了4种更为安全的强制类型转换:

  • static_cast显式转换类型;
  • dynamic_cast显式转换类型;
  • const_cast显式转换类型;
  • reinterpret_cast显式转换类型;

C++强制类型转换通用书写形式:强制类型转换名 <type> ( express );

  • 强制类型转换名:static_castdynamic_castconst_castreinterpret_cast4种类型
  • type:转换到的目标类型
  • express:待转换的类型(值或变量),即express转换成type类型

每一种强制类型转换都有其特定的目的,这样可以提供更丰富的含义和功能、更好的类型检查机制,方便代码的编写和维护。


static_cast显示转换

含义:static_cast静态转换,理解成“正常转换”(编译时进行类型检查)。

适用场合:

1、相关类型转换,比如整型int和实型qeal之间的转换。

double f = 100.34;
int ic1 = f;                     //方式一:隐式转换直接转
int ic1 = int(f);                //方式二:C风格的强制类型转换
int ic2 = static_cast<int>(f);   //方式三:C++风格的强制类型转换

2、继承关系中子类转换成父类类型(向上转换:隐式转换),可以直接转换,也可以使用static_cast转。

class A {};
class B : public A {};
A a;
B b;
a = b;                  //方式一:隐式转换直接转
a = static_cast<A>(b);  //方式二:static_cast转换

3、void *与其他类型指针之间的转换。void *:无类型指针(万能指针):可以指向任何指针类型

int i = 10;
int *p = &i;
void *q = p;                       //方式一:隐式转换,系统内部自己转
void *q = static_cast<void *>(p);  //方式二:static_cast转换

不适用场合:

1、指针类型之间的转换,比如int *double *double *float *等。

double d = 100.34;
double *pd = &d;
int *pi = static_cast<int *>(pd);   //达咩,类型转换无效

小结:static_cast含义跟C语言中的强制类型转换差不多:

  • C风格的强制类型转换编译器自己能够进行的隐式类型转换都可以用static_cast显示完成转换(一般隐式转换让系统内部自己转就好了,不需要static_cast转换)。
  • C风格的强制类型转换一样,使用static_cast也要保证转换的安全性和正确性,比如int i = (int)“asa”这种情形不应该出现。

dynamic_cast显示转换

含义:dynamic_cast:能够将基类指针引用安全的转换为派生类的指针引用(运行时进行类型检查)。

ps:只要dynamic_cast运算符能转换成功,就说明这个指针实际上是要转换到的那个类型,否则不能转换成功。这也间接的帮助我们完成了运行时的类型识别和安全检查(弥补C语言风格的强制转换式的不足)。

适用场合:

1、有继承关系父类指针转换成子类指针(向下显示转换)

struct Father{ /*父类Father*/ };
struct Son: Father{ /*基类Father的子类B*/ };
Father *father = new Son;
Son *son = (Son *)(father);  //C风格的强制类型转换:硬把父类指针Father *转换成子类指针Son *,可以
//son->子类成员               //可以,能够正常调用Son类的成员函数

这种转换需要我们明确父类指针father是指向子类Son对象的(Father *father = new Son)才是安全的。否则明明father是指向Son对象的,却被强制转换成其他类的指针,再调用就不安全了。此外,如果在他人的代码中看到一个指针father,想要确认这个指针到底是指向本身类Father对象的、子类Son对象还是其他类的对象就太好区分,所以应该使用dynamic_cast来完成父类指针到子类指针的强制转换:

struct Father{ /*父类Father*/ };
struct Son: Father{ /*基类Father的子类B*/ };
Father *father = new Son; 
Son *son =  dynamic_cast<Son *>(father);
if (son != nullptr)     //对于引用,如果用dynamic_cast转换失败,系统会抛出std::bad_cast异常
{
    cout << "father实际是一个Son类型" << endl;  //在这里操作Son里面的成员函数、成员变量都能够操作并且安全的操作
}

注意:使用dynamic_cast显示转换父类指针时要保证父类中一定要有虚函数virtual,否则会报错:运行dynamic_cast操作符必须包含多态类类型。

2、有继承关系父类引用转换成子类引用(用的较少,了解)

struct Father{ /*父类Father*/ };
struct Son: Father{ /*基类Father的子类B*/ };
Father *father1 = new Son;
Father &father2 = *father1;
try
{
    Son &son = dynamic_cast<Son &>(father2);    //转换成功
}
catch (std::bad_cast)
{
    cout << "father2实际不是一个Son类型" << endl;
}

const_cast显示转换

含义:const_cast去除指针或引用的const属性。该转换能够将const性质转换掉或去除掉(编译时进行类型检查)。

错误示范:

const int ai = 90;
int ai2 = const_cast<int>(ai);

结果:

正确示范:

const int ai = 90;
const int *ai2 = &ai;
int *ai3 = const_cast<int *>(ai2);   //截止到这里都没问题
*ai3 = 120;  //注意:这种写值行为是一种未定义行为(实际结果不可控,因为*ai3转换之前是常量const int ai,所以不要往里写值,正常调用指针对象没问题)

reinterpret_cast显示转换

reinterpret_cast 处理无关类型之间的转换,也就是两个转换类型之间没有什么关系,就等于可以乱转、自由转,非常随意(编译时进行类型检查)

ps: reinterpret:重新解释,将操作数内容解释为另一种不同的类型(能把操作数的类型都转了)。

适用场合:

1、将一个整型(地址)转换成指针,一种类型指针转换成另一种类型指针,按照转换后的类型重新解释内存中的内容。

2、将一个指针类型转换成一个整型。

int i = 10;
int *p= &i;
int *pi = reinterpret_cast<int *>(&i);    //这个没啥说的
char *pc = reinterpret_cast<char *>(p);   //int *也可以转换成char *,语法对,但是没什么屌用

ps:reinterpret_cast被认为是危险的类型转换,虽然其可以随便转换,而且编译器也不报错,但是需要合乎规则的用,不然就没什么意义了。这里了解一下,防止看别人代码不知道什么意思。


三. 总结

  1. 所有强制类型转换,不建议使用,强制类型转换能够抑制编译器报错。
  2. 学习目的:认识这些类型转换符,方便大家阅读别人代码。
  3. 资料说:reinterpret_cast危险,我们需要合乎规则的用,不要乱用;使用const_cast意味着设计缺陷。
  4. 如果实在需要使用强制类型转换,不要再使用C语言风格的类型转换了,而是用上面C++风格的类型转换,一般static_castreinterpret_cast能够很好的取代C语言风格的强制类型转换。

下雨天,最惬意的事莫过于躺在床上静静听雨,雨中入眠,连梦里也长出青苔。


目录
相关文章
|
4天前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
24 5
|
1月前
|
编译器 C++
C++入门12——详解多态1
C++入门12——详解多态1
38 2
C++入门12——详解多态1
|
1月前
|
C++
C++入门13——详解多态2
C++入门13——详解多态2
79 1
|
1月前
|
存储 安全 编译器
【C++打怪之路Lv1】-- 入门二级
【C++打怪之路Lv1】-- 入门二级
23 0
|
1月前
|
自然语言处理 编译器 C语言
【C++打怪之路Lv1】-- C++开篇(入门)
【C++打怪之路Lv1】-- C++开篇(入门)
26 0
|
1月前
|
分布式计算 Java 编译器
【C++入门(下)】—— 我与C++的不解之缘(二)
【C++入门(下)】—— 我与C++的不解之缘(二)
|
1月前
|
编译器 Linux C语言
【C++入门(上)】—— 我与C++的不解之缘(一)
【C++入门(上)】—— 我与C++的不解之缘(一)
|
1月前
|
编译器 C++
C++入门11——详解C++继承(菱形继承与虚拟继承)-2
C++入门11——详解C++继承(菱形继承与虚拟继承)-2
30 0
|
1月前
|
程序员 C++
C++入门11——详解C++继承(菱形继承与虚拟继承)-1
C++入门11——详解C++继承(菱形继承与虚拟继承)-1
32 0
|
1月前
|
存储 算法 C++
C++入门10——stack与queue的使用
C++入门10——stack与queue的使用
42 0