C++四种强制类型转换:static_cast、const_cast、reinterpret_cast和dynamic_cast

2024-04-17 05:32

本文主要是介绍C++四种强制类型转换:static_cast、const_cast、reinterpret_cast和dynamic_cast,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

C风格的强制转换

在C++基本的数据类型中,可以分为四类:整型,浮点型,字符型,布尔型。其中数值型包括 整型与浮点型;字符型即为char。

(1)将浮点型数据赋值给整型变量时,舍弃其小数部分。
(2)将整型数据赋值给浮点型变量时,数值不变,整型提升。
(3)将double型数据赋值给float型变量时,注意数值范围溢出。
(4)字符型数据可以赋值给整型变量,此时存入的是字符的ASCII码。
(5)将一个int,short或long型数据赋值给一个char型变量,只将低8位原封不动的送到char型变量中。
(6)将有符号型数据赋值给长度相同的无符号型变量,连同原来的符号位一起传送。

C++强制类型转换

在C++语言中新增了四个关键字static_cast、const_cast、reinterpret_cast和dynamic_cast。这四个关键字都是用于强制类型转换的。新类型的强制转换可以提供更好的控制强制转换过程,允许控制各种不同种类的强制转换。
C++中风格是static_cast(content)。C++风格的强制转换其他的好处是,它们能更清晰的表明它们要干什么。程序员只要扫一眼这样的代码,就能立即知道一个强制转换的目的。

1.static_cast:相干类型之间的转换
在C++语言中static_cast用于数据类型的强制转换,强制将一种数据类型转换为另一种数据类型。例如将整型数据转换为浮点型数据。
[例]C语言所采用的类型转换方式:

int a = 10;
int b = 3;
double result = (double)a / (double)b;

将整型变量a和b转换为双精度浮点型,然后相除。在C++语言中,我们可以采用static_cast关键字来进行强制类型转换,如下所示。

int a = 10;
int b = 3;
double result = static_cast<double>(a) / static_cast<double>(b);

2.const_cast:强制去掉这种不能被修改的常属性,但需要特别注意的是const_cast不是用于去除变量的常量性,而是去除指向常数对象的指针或引用的常量性,其去除常量性的对象必须为指针或引用。
[例]一个错误的例子:

const int a = 10;
const int * p = &a;
*p = 20;                  //compile error
int b = const_cast<int>(a);  //compile error

在本例中出现了两个编译错误,第一个编译错误是*p因为具有常量性,其值是不能被修改的;另一处错误是const_cast强制转换对象必须为指针或引用,而例3中为一个变量,这是不允许的!

#include<iostream>
using namespace std;int main()
{const int a = 10;const int * p = &a;int *q;q = const_cast<int *>(p);*q = 20;    //finecout <<a<<" "<<*p<<" "<<*q<<endl;cout <<&a<<" "<<p<<" "<<q<<endl;return 0;
}

在本例中,我们将变量a声明为常量变量,同时声明了一个const指针指向该变量(此时如果声明一个普通指针指向该常量变量的话是不允许的,Visual Studio 2010编译器会报错)。之后我们定义了一个普通的指针*q。将p指针通过const_cast去掉其常量性,并赋给q指针。之后我再修改q指针所指地址的值时,这是不会有问题的。

最后将结果打印出来,运行结果如下:
10 20 20
002CFAF4 002CFAF4 002CFAF4

3.reinterpret_cast:不相干类型的转换
reinterpret_cast主要有三种强制转换用途:改变指针或引用的类型、将指针或引用转换为一个足够长度的整形、将整型转换为指针或引用类型。
[例].将整型指针通过reinterpret_cast强制转换成了双精度浮点型指针。

//将整型指针通过reinterpret_cast强制转换成了双精度浮点型指针。
int *a = new int;
double *d = reinterpret_cast<double *>(a);

4. dynamic_cast:它要求所转换的操作数必须包含多态类类型(即至少包含一个虚函数的类);<>内部所描述的目标类型必须为指针或引用。
这里提出一个概念RTTI(运行时类型信息):通过运行时类型信息程序能够使用基类的指针或引用来检查这些指针或引用所指的对象的实际派生类型。
(1)其他三种都是编译时完成的,dynamic_cast是运行时处理的,运行时要进行类型检查。
(2)不能用于内置的基本数据类型的强制转换。
(3)dynamic_cast转换如果成功的话返回的是指向类的指针或引用,转换失败的话则会返回NULL。
(4)使用dynamic_cast进行转换的,基类中一定要有虚函数,否则编译不通过。 Base类中需要检测有虚函数的原因:类中存在虚函数,就说明它有想要让基类指针或引用指向派生类对象的情况,此时转换才有意义。
(5)在类的转换时,在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的。在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。

    向上转换,即为子类指针指向父类指针(一般不会出问题);向下转换,即将父类指针转化子类指针。向下转换的成功与否还与将要转换的类型有关,即要转换的指针指向的对象的实际类型与转换以后的对象类型一定要相同,否则转换失败。

在C++中,编译期的类型转换有可能会在运行时出现错误,特别是涉及到类对象的指针或引用操作时,更容易产生错误。Dynamic_cast操作符则可以在运行期对可能产生问题的类型转换进行测试。

[例]一个错误的例子

#include<iostream>
using namespace std;class base
{
public :void m(){cout<<"m"<<endl;}
};class derived : public base
{
public:void f(){cout<<"f"<<endl;}
};int main()
{derived * p;p = new base;p = dynamic_cast<derived *>(new base);p->m();p->f();return 0;
}

在本例中利用dynamic_cast进行强制类型转换,但是因为base类中并不存在虚函数,因此p = dynamic_cast<derived *>(new base);这一句会编译错误。

为了解决本例中的语法错误,我们可以将base类中的函数m声明为虚函数,virtual void m(){cout<<“m”<<endl;}。

[例]正确示范

#include<iostream>
#include<cstring>using namespace std;class A
{public:virtual void f(){cout<<"hello"<<endl;};
};class B:public A
{public:void f(){cout<<"hello2"<<endl;};};class C
{void pp(){return;}
};int fun()
{return 1;
}int main()
{A* a1=new B;//a1是A类型的指针指向一个B类型的对象A* a2=new A;//a2是A类型的指针指向一个A类型的对象B* b;C* c;//结果为not null,向下转换成功,a1之前指向的就是B类型的对象,// 所以可以转换成B类型的指针。b=dynamic_cast<B*>(a1);if(b==NULL){cout<<"null"<<endl;}else{cout<<"not null"<<endl;}b=dynamic_cast<B*>(a2);//结果为null,向下转换失败if(b==NULL){cout<<"null"<<endl;}else{cout<<"not null"<<endl;}c=dynamic_cast<C*>(a);//结果为null,向下转换失败if(c==NULL){cout<<"null"<<endl;}else{cout<<"not null"<<endl;}delete(a);return 0;
}

这篇关于C++四种强制类型转换:static_cast、const_cast、reinterpret_cast和dynamic_cast的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/910905

相关文章

Windows下C++使用SQLitede的操作过程

《Windows下C++使用SQLitede的操作过程》本文介绍了Windows下C++使用SQLite的安装配置、CppSQLite库封装优势、核心功能(如数据库连接、事务管理)、跨平台支持及性能优... 目录Windows下C++使用SQLite1、安装2、代码示例CppSQLite:C++轻松操作SQ

C++中RAII资源获取即初始化

《C++中RAII资源获取即初始化》RAII通过构造/析构自动管理资源生命周期,确保安全释放,本文就来介绍一下C++中的RAII技术及其应用,具有一定的参考价值,感兴趣的可以了解一下... 目录一、核心原理与机制二、标准库中的RAII实现三、自定义RAII类设计原则四、常见应用场景1. 内存管理2. 文件操

C++中零拷贝的多种实现方式

《C++中零拷贝的多种实现方式》本文主要介绍了C++中零拷贝的实现示例,旨在在减少数据在内存中的不必要复制,从而提高程序性能、降低内存使用并减少CPU消耗,零拷贝技术通过多种方式实现,下面就来了解一下... 目录一、C++中零拷贝技术的核心概念二、std::string_view 简介三、std::stri

C++高效内存池实现减少动态分配开销的解决方案

《C++高效内存池实现减少动态分配开销的解决方案》C++动态内存分配存在系统调用开销、碎片化和锁竞争等性能问题,内存池通过预分配、分块管理和缓存复用解决这些问题,下面就来了解一下... 目录一、C++内存分配的性能挑战二、内存池技术的核心原理三、主流内存池实现:TCMalloc与Jemalloc1. TCM

C++ 函数 strftime 和时间格式示例详解

《C++函数strftime和时间格式示例详解》strftime是C/C++标准库中用于格式化日期和时间的函数,定义在ctime头文件中,它将tm结构体中的时间信息转换为指定格式的字符串,是处理... 目录C++ 函数 strftipythonme 详解一、函数原型二、功能描述三、格式字符串说明四、返回值五

C++作用域和标识符查找规则详解

《C++作用域和标识符查找规则详解》在C++中,作用域(Scope)和标识符查找(IdentifierLookup)是理解代码行为的重要概念,本文将详细介绍这些规则,并通过实例来说明它们的工作原理,需... 目录作用域标识符查找规则1. 普通查找(Ordinary Lookup)2. 限定查找(Qualif

C/C++ chrono简单使用场景示例详解

《C/C++chrono简单使用场景示例详解》:本文主要介绍C/C++chrono简单使用场景示例详解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友... 目录chrono使用场景举例1 输出格式化字符串chrono使用场景China编程举例1 输出格式化字符串示

C++/类与对象/默认成员函数@构造函数的用法

《C++/类与对象/默认成员函数@构造函数的用法》:本文主要介绍C++/类与对象/默认成员函数@构造函数的用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录名词概念默认成员函数构造函数概念函数特征显示构造函数隐式构造函数总结名词概念默认构造函数:不用传参就可以

C++类和对象之默认成员函数的使用解读

《C++类和对象之默认成员函数的使用解读》:本文主要介绍C++类和对象之默认成员函数的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、默认成员函数有哪些二、各默认成员函数详解默认构造函数析构函数拷贝构造函数拷贝赋值运算符三、默认成员函数的注意事项总结一

C/C++中OpenCV 矩阵运算的实现

《C/C++中OpenCV矩阵运算的实现》本文主要介绍了C/C++中OpenCV矩阵运算的实现,包括基本算术运算(标量与矩阵)、矩阵乘法、转置、逆矩阵、行列式、迹、范数等操作,感兴趣的可以了解一下... 目录矩阵的创建与初始化创建矩阵访问矩阵元素基本的算术运算 ➕➖✖️➗矩阵与标量运算矩阵与矩阵运算 (逐元