C++ 强制类型转换符(static_cast、reinterpret_cast、const_cast和dynamic_cast)

2024-03-12 01:44

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

将类型名作为强制类型转换运算符的做法是C语言的老式做法,C++ 为保持兼容而予以保留。

C++ 引入了四种功能不同的强制类型转换运算符以进行强制类型转换:static_cast、reinterpret_cast、const_cast 和 dynamic_cast。

强制类型转换是有一定风险的,有的转换并不一定安全,如把整型数值转换成指针,把基类指针转换成派生类指针,把一种函数指针转换成另一种函数指针,把常量指针转换成非常量指针等。C++ 引入新的强制类型转换机制,主要是为了克服C语言强制类型转换的以下三个缺点。

1) 没有从形式上体现转换功能和风险的不同。

例如,将 int 强制转换成 double 是没有风险的,而将常量指针转换成非常量指针,将基类指针转换成派生类指针都是高风险的,而且后两者带来的风险不同(即可能引发不同种类的错误),C语言的强制类型转换形式对这些不同并不加以区分。

2) 将多态基类指针转换成派生类指针时不检查安全性,即无法判断转换后的指针是否确实指向一个派生类对象。

3) 难以在程序中寻找到底什么地方进行了强制类型转换。

强制类型转换是引发程序运行时错误的一个原因,因此在程序出错时,可能就会想到是不是有哪些强制类型转换出了问题。

如果采用C语言的老式做法,要在程序中找出所有进行了强制类型转换的地方,显然是很麻烦的,因为这些转换没有统一的格式。

而用 C++ 的方式,则只需要查找_cast字符串就可以了。甚至可以根据错误的类型,有针对性地专门查找某一种强制类型转换。例如,怀疑一个错误可能是由于使用了 reinterpret_cast 导致的,就可以只查找reinterpret_cast字符串。

C++ 强制类型转换运算符的用法如下:

强制类型转换运算符 <要转换到的类型> (待转换的表达式)

例如:

double d = static_cast <double> (3*5);  //将 3*5 的值转换成实数

下面分别介绍四种强制类型转换运算符。

static_cast

static_cast 用于进行比较“自然”和低风险的转换,如整型和浮点型、字符型之间的互相转换。另外,如果对象所属的类重载了强制类型转换运算符 T(如 T 是 int、int* 或其他类型名),则 static_cast 也能用来进行对象到 T 类型的转换。

static_cast 不能用于在不同类型的指针之间互相转换,也不能用于整型和指针之间的互相转换,当然也不能用于不同类型的引用之间的转换。因为这些属于风险比较高的转换。

static_cast 用法示例如下:

    #include <iostream>using namespace std;class A{public:operator int() { return 1; }operator char*() { return NULL; }};int main(){A a;int n;char* p = "New Dragon Inn";n = static_cast <int> (3.14);  // n 的值变为 3n = static_cast <int> (a);  //调用 a.operator int,n 的值变为 1p = static_cast <char*> (a);  //调用 a.operator char*,p 的值变为 NULLn = static_cast <int> (p);  //编译错误,static_cast不能将指针转换成整型p = static_cast <char*> (n);  //编译错误,static_cast 不能将整型转换成指针return 0;}

reinterpret_cast

reinterpret_cast 用于进行各种不同类型的指针之间、不同类型的引用之间以及指针和能容纳指针的整数类型之间的转换。转换时,执行的是逐个比特复制的操作。

这种转换提供了很强的灵活性,但转换的安全性只能由程序员的细心来保证了。例如,程序员执意要把一个 int* 指针、函数指针或其他类型的指针转换成 string* 类型的指针也是可以的,至于以后用转换后的指针调用 string 类的成员函数引发错误,程序员也只能自行承担查找错误的烦琐工作:(C++ 标准不允许将函数指针转换成对象指针,但有些编译器,如 Visual Studio 2010,则支持这种转换)。

reinterpret_cast 用法示例如下:

#include <iostream>
using namespace std;
class A
{
public:int i;int j;A(int n):i(n),j(n) { }
};
int main()
{A a(100);int &r = reinterpret_cast<int&>(a); //强行让 r 引用 ar = 200;  //把 a.i 变成了 200cout << a.i << "," << a.j << endl;  // 输出 200,100int n = 300;A *pa = reinterpret_cast<A*> ( & n); //强行让 pa 指向 npa->i = 400;  // n 变成 400pa->j = 500;  //此条语句不安全,很可能导致程序崩溃cout << n << endl;  // 输出 400long long la = 0x12345678abcdLL;pa = reinterpret_cast<A*>(la); //la太长,只取低32位0x5678abcd拷贝给paunsigned int u = reinterpret_cast<unsigned int>(pa);//pa逐个比特拷贝到ucout << hex << u << endl;  //输出 5678abcdtypedef void (* PF1) (int);typedef int (* PF2) (int,char *);PF1 pf1;  PF2 pf2;pf2 = reinterpret_cast<PF2>(pf1); //两个不同类型的函数指针之间可以互相转换
}

程序的输出结果是:
200, 100
400
5678abed

第 19 行的代码不安全,因为在编译器看来,pa->j 的存放位置就是 n 后面的 4 个字节。 本条语句会向这 4 个字节中写入 500。但这 4 个字节不知道是用来存放什么的,贸然向其中写入可能会导致程序错误甚至崩溃。

上面程序中的各种转换都没有实际意义,只是为了演示 reinteipret_cast 的用法而已。在编写黑客程序、病毒或反病毒程序时,也许会用到这样怪异的转换。

reinterpret_cast体现了 C++ 语言的设计思想:用户可以做任何操作,但要为自己的行为负责。

const_cast

const_cast 运算符仅用于进行去除 const 属性的转换,它也是四个强制类型转换运算符中唯一能够去除 const 属性的运算符。

将 const 引用转换为同类型的非 const 引用,将 const 指针转换为同类型的非 const 指针时可以使用 const_cast 运算符。例如:

const string s = "Inception";
string& p = const_cast <string&> (s);
string* ps = const_cast <string*> (&s); // &s 的类型是 const string*

dynamic_cast

用 reinterpret_cast 可以将多态基类(包含虚函数的基类)的指针强制转换为派生类的指针,但是这种转换不检查安全性,即不检查转换后的指针是否确实指向一个派生类对象。dynamic_cast专门用于将多态基类的指针或引用强制转换为派生类的指针或引用,而且能够检查转换的安全性。对于不安全的指针转换,转换结果返回 NULL 指针。

dynamic_cast 是通过“运行时类型检查”来保证安全性的。dynamic_cast 不能用于将非多态基类的指针或引用强制转换为派生类的指针或引用——这种转换没法保证安全性,只好用 reinterpret_cast 来完成。

dynamic_cast 示例程序如下:

#include <iostream>
#include <string>
using namespace std;
class Base
{  //有虚函数,因此是多态基类
public:virtual ~Base() {}
};
class Derived : public Base { };
int main()
{Base b;Derived d;Derived* pd;pd = reinterpret_cast <Derived*> (&b);if (pd == NULL)//此处pd不会为 NULL。reinterpret_cast不检查安全性,总是进行转换cout << "unsafe reinterpret_cast" << endl; //不会执行pd = dynamic_cast <Derived*> (&b);if (pd == NULL)  //结果会是NULL,因为 &b 不指向派生类对象,此转换不安全cout << "unsafe dynamic_cast1" << endl;  //会执行pd = dynamic_cast <Derived*> (&d);  //安全的转换if (pd == NULL)  //此处 pd 不会为 NULLcout << "unsafe dynamic_cast2" << endl;  //不会执行return 0;
}

程序的输出结果是:
unsafe dynamic_cast1

第 20 行,通过判断 pd 的值是否为 NULL,就能知道第 19 行进行的转换是否是安全的。第 23 行同理。

如果上面的程序中出现了下面的语句:

Derived & r = dynamic_cast <Derived &> (b);

那该如何判断该转换是否安全呢?不存在空引用,因此不能通过返回值来判断转换是否安全。C++ 的解决办法是:dynamic_cast 在进行引用的强制转换时,如果发现转换不安全,就会拋出一个异常,通过处理异常,就能发现不安全的转换。

转自:C++强制类型转换运算符(static_cast、reinterpret_cast、const_cast和dynamic_cast)

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



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

相关文章

C++ 中的 if-constexpr语法和作用

《C++中的if-constexpr语法和作用》if-constexpr语法是C++17引入的新语法特性,也被称为常量if表达式或静态if(staticif),:本文主要介绍C++中的if-c... 目录1 if-constexpr 语法1.1 基本语法1.2 扩展说明1.2.1 条件表达式1.2.2 fa

C语言中的数据类型强制转换

《C语言中的数据类型强制转换》:本文主要介绍C语言中的数据类型强制转换方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C语言数据类型强制转换自动转换强制转换类型总结C语言数据类型强制转换强制类型转换:是通过类型转换运算来实现的,主要的数据类型转换分为自动转换

Java强制转化示例代码详解

《Java强制转化示例代码详解》:本文主要介绍Java编程语言中的类型转换,包括基本类型之间的强制类型转换和引用类型的强制类型转换,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录引入基本类型强制转换1.数字之间2.数字字符之间引入引用类型的强制转换总结引入在Java编程语言中,类型转换(无论

C++中::SHCreateDirectoryEx函数使用方法

《C++中::SHCreateDirectoryEx函数使用方法》::SHCreateDirectoryEx用于创建多级目录,类似于mkdir-p命令,本文主要介绍了C++中::SHCreateDir... 目录1. 函数原型与依赖项2. 基本使用示例示例 1:创建单层目录示例 2:创建多级目录3. 关键注

C++从序列容器中删除元素的四种方法

《C++从序列容器中删除元素的四种方法》删除元素的方法在序列容器和关联容器之间是非常不同的,在序列容器中,vector和string是最常用的,但这里也会介绍deque和list以供全面了解,尽管在一... 目录一、简介二、移除给定位置的元素三、移除与某个值相等的元素3.1、序列容器vector、deque

C++常见容器获取头元素的方法大全

《C++常见容器获取头元素的方法大全》在C++编程中,容器是存储和管理数据集合的重要工具,不同的容器提供了不同的接口来访问和操作其中的元素,获取容器的头元素(即第一个元素)是常见的操作之一,本文将详细... 目录一、std::vector二、std::list三、std::deque四、std::forwa

C++字符串提取和分割的多种方法

《C++字符串提取和分割的多种方法》在C++编程中,字符串处理是一个常见的任务,尤其是在需要从字符串中提取特定数据时,本文将详细探讨如何使用C++标准库中的工具来提取和分割字符串,并分析不同方法的适用... 目录1. 字符串提取的基本方法1.1 使用 std::istringstream 和 >> 操作符示

C++原地删除有序数组重复项的N种方法

《C++原地删除有序数组重复项的N种方法》给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度,不要使用额外的数组空间,你必须在原地修改输入数组并在使用O(... 目录一、问题二、问题分析三、算法实现四、问题变体:最多保留两次五、分析和代码实现5.1、问题分析5.

C++ 各种map特点对比分析

《C++各种map特点对比分析》文章比较了C++中不同类型的map(如std::map,std::unordered_map,std::multimap,std::unordered_multima... 目录特点比较C++ 示例代码 ​​​​​​代码解释特点比较1. std::map底层实现:基于红黑

C++中函数模板与类模板的简单使用及区别介绍

《C++中函数模板与类模板的简单使用及区别介绍》这篇文章介绍了C++中的模板机制,包括函数模板和类模板的概念、语法和实际应用,函数模板通过类型参数实现泛型操作,而类模板允许创建可处理多种数据类型的类,... 目录一、函数模板定义语法真实示例二、类模板三、关键区别四、注意事项 ‌在C++中,模板是实现泛型编程