您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > C/C++/C#

C++ 的强制类型转换

时间:2019-09-09 11:26:38  来源:  作者:

作者 | 樱雨楼

责编 | 屠敏

出品 | CSDN(ID:CSDNnews)

在上篇与中篇中,我们讨论了隐式类型转换及其与函数重载之间的相关话题。本篇将要讨论的即为类型转换的另一大分支——强制类型转换。

1.C风格的强制类型转换

C语言中,强制类型转换存在两种等价形式:Type(Value)或(Type)Value。

参考以下代码:

int main
{
(int *) malloc(0); // (Type)Value形式的强制类型转换
int(0.); // Type(Value)形式的强制类型转换
}

上述代码中,我们分别使用了C语言的提供的两种强制类型转换的等价形式将void *转为了int *,以及将double转为了int。

2.static_cast

在C++中,staticcast相当于C语言中的强制类型转换语法。staticcast用于在编译期对某种类型的变量进行强制类型转换。

参考以下代码:

int main
{
static_cast<int *>(malloc(0));
static_cast<int>(0.);
}

上述代码中,我们使用了static_cast分别将void *转为了int *,以及将double转为了int。

3.const_cast

constcast是C++中专用于处理与const相关的强制类型转换关键字,其功能为:为一个变量重新设定其const描述。即:constcast可以为一个变量强行增加或删除其const限定。

需要明确的是,即使用户通过constcast强行去除了const属性,也不代表当前变量从不可变变为了可变。constcast只是使得用户接管了编译器对于const限定的管理权,故用户必须遵守“不修改变量”的承诺。如果违反此承诺,编译器也不会因此而引发编译时错误,但可能引发运行时错误。

下面讨论const_cast的主要用途。

考察以下代码:

struct A { A &test { return *this; } };

int main
{
A.test;
}

这段代码看上去运行正常。但如果:

struct A { A &test { return *this; } };

int main
{
const A a;
a.test; // Error!
}

我们试图用一个const对象去调用非const成员函数,此时,为了调用此成员函数,const A *this就需要转换为A *this,这显然是不行的。

经过上述讨论,我们可以将代码修改为如下:

struct A { const A &test const { return *this; } };

int main
{
const A a;
a.test;
}

我们将this指针声明为const A *,解决了此问题。但不难发现,如果我们通过一个非const对象调用此方法,其返回值也会被转为const,从而不再可以继续调用任何接受A *this的成员函数。这明显不是我们想要的结果:

struct A
{
const A &test const { return *this; }
A &test2 { return *this; }
};

int main
{
A.test.test2; // Error!
}

怎么解决此问题呢?根据C++函数重载的规则,我们可以为test成员函数同时定义const与非const版本:

struct A
{
A &test { return *this; }
const A &test const { return *this; }
A &test2 { return *this; }
};

int main
{
A.test.test2;

const A a;
a.test;
}

对于A的非const实例而言,test的非const版本是精确匹配,故编译器将选择此版本,从而返回一个A &;同时,对于A的const实例而言,const版本的test是其唯一可用的版本,返回一个const A &。

至此,问题解决了。我们基于const的有无重载出了两个版本的成员函数,从而使得const对象与非const对象能够各自调用不同的版本,互不影响。

在实际情况中,我们定义的两个版本的重载函数除了有无const以外往往没有任何区别,此时就可以使用const_cast定义第二个重载版本,而无需写两遍一模一样的函数体。

参考以下代码:

struct A
{
A &test { ... }

// 通过const_cast强行去除this的const限定后调用非const版本
// 返回值通过隐式类型转换再转回const A &
const A &test const { return const_cast<A *>(this)->test; }
};

上述代码中,我们首先定义了一个非const版本的test成员函数,这个成员函数将提供给A *this调用;在定义test成员函数的const版本时,我们通过const_cast<A *>(this),将此版本的const A *this指针转换为非const版本需要的A *this类型指针,然后调用了非const版本的test成员函数,并返回其调用结果,非const版本的test成员函数的返回值将通过隐式类型转换转为const A &。

由此可见,通过const_cast,我们仅需一行代码就可以完成第二个函数重载版本的定义。

4.dynamic_cast

上文提到,动态类型为继承类的指针或引用可以存储在静态类型为基类的变量中,且不会发生隐式类型转换。对于一个变量而言,虽然其动态类型确实是继承类,但由于编译期与运行期的差别,其也无法跨越“可使用的成员名称由静态类型决定”这一规则。

虽然继承类可以通过虚函数的方式一定程度上解决此种情况,但是,如果某个成员函数不是基类虚函数,而只存在于继承类中呢?dynamic_cast为我们提供了解决方案。

当一个静态类型为基类指针或引用的变量确实存放了继承类指针或引用时,从基类向继承类的类型转换,即向下类型转换理论上是可行的,dynamic_cast即用于在运行时实现向下类型转换。

需要注意的是,dynamic_cast的使用必须同时满足以下所有条件:

  1. 被转换的变量的类型为基类指针或引用,且其确实存放了一个继承类指针或引用

  2. 基类具有虚表,即基类必须至少定义了一个虚函数

参考以下代码:

struct A { virtual void test {} }; // 基类含有虚函数
struct B: A { void test2 {} }; // 继承类特有函数

int main
{
// 静态类型为基类指针的变量存放继承类指针
A *b = new B;

// 通过向下类型转换调用继承类特有函数
dynamic_cast<B *>(b)->test2;
}

上述代码中,我们首先定义了具有虚函数的基类A,然后定义了具有继承类特有函数的类B。此时,由于test2成员函数并未在基类中注册为虚函数,我们将无法通过静态类型为A *的变量b调用此函数。但由于我们可以确定变量b的动态类型为B *,则可以于运行时通过dynamic_cast将变量b的静态类型转为B *,然后调用继承类的特有函数test2。

5.reinterpret_cast

reinterpret,即“重新解释”,顾名思义,这个强制类型转换的作用是提供某个变量在底层数据上的重新解释。当我们对一个变量使用reinterpretcast后,编译器将无视任何不合理行为,强行将被转换变量的内存数据重解释为某个新的类型。需要注意的是,reinterpretcast要求转换前后的类型所占用内存大小一致,否则将引发编译时错误。

参考以下代码:

int main
{
reinterpret_cast<int *>(0); // 强行将一个整数的内存数据解释为一个int *
}

6.讨论

编程语言的强类型与弱类型相关话题,多年来业界一直讨论不休,有的语言发展出了高度弱类型的语法体系,而有的语言则相对严谨,要求用户尽可能多的使用显式类型转换。C++作为一门经典的弱类型语言,其类型转换的相关话题自然十分庞大。

纵观C++的类型转换语法体系,其延续了C++一贯的包罗万象风格,不仅为用户提供了自定义类型转换的极大自由度,也在语法层面为类型转换可能会带来的各种错综复杂的情况作出了严谨的规定。

保守看来,如果对C++的类型转换没有深入的理解,或不希望大量使用隐式类型转换时,我们不应过度的依赖诸如非explicit转换构造函数,自定义的类型转换操作符,以及涉及隐式类型转换的各种重载确定等语法组分。但作为C++语法体系的一个重要部分,深入理解C++关于类型转换的各种话题,必定是十分重要的。

作者简介:樱雨楼,毕业于生物信息学专业,是一枚Python/C++/Perl开发,自称R语言黑粉,GitHub勾搭:https://github.com/yingyulou



Tags: C++   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
何为日志框架日志框架:一个经过专门设计的实用程序,用于规范应用程序的日志记录过程。日志框架可以自己编写(技术要牛才行哦),也可以由第三方(例如:log4cplus)提供。对于不同的日志...【详细内容】
2021-03-04  Tags: C++  点击:(248)  评论:(0)  加入收藏
70年代初,贝尔实验室创建了C语言,它是开发UNIX的副产品。很快C就成为了...【详细内容】
2020-09-14  Tags: C++  点击:(57)  评论:(0)  加入收藏
作者 | 繁星蓝雨责编 | 夕颜头图 | CSDN 下载自视觉中国出品 | CSDN(ID:CSDNnews) 常见观点可以轻易的找出许多文献说明C++太复杂了,例如学习C++的书籍的厚度。这样以至于C++的...【详细内容】
2020-05-10  Tags: C++  点击:(99)  评论:(0)  加入收藏
相对其他语言来说,C++ 算是难度比较高的了,这一点无法否认。但是如果能有一些好的网站,则会让 C++ 的学习事半功倍。那就来介绍几个最常用的(最好的)吧,包含了参考手册、教程、框...【详细内容】
2020-04-01  Tags: C++  点击:(155)  评论:(0)  加入收藏
相对其他语言来说,C++ 算是难度比较高的了,这一点无法否认。但是如果能有一些好的网站,则会让 C++ 的学习事半功倍。那就来介绍几个最常用的(最好的)吧,包含了参考手册、教程、框...【详细内容】
2020-03-12  Tags: C++  点击:(109)  评论:(0)  加入收藏
什么是 ORM?关于 ORM,维基百科描述如下:ORM 全称是 Object Relational Mapping(对象关系映射),是一种程序设计技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。从效...【详细内容】
2020-02-24  Tags: C++  点击:(211)  评论:(0)  加入收藏
C程序是一组函数和数据类型,就像一把锋利的随身匕首,非常灵活,在高手的手上可以幻化出各种招式,杀人于无形。C++程序是一组函数和类,像一门大炮,扛在肩上很重,炮弹打出去威力很大,但...【详细内容】
2019-12-20  Tags: C++  点击:(124)  评论:(0)  加入收藏
不同的数据在计算机内存中的存储方式不同,导致了“类型”这一抽象概念的出现。...【详细内容】
2019-09-24  Tags: C++  点击:(114)  评论:(0)  加入收藏
在上篇与中篇中,我们讨论了隐式类型转换及其与函数重载之间的相关话题。本篇将要讨论的即为类型转换的另一大分支——强制类型转换。...【详细内容】
2019-09-09  Tags: C++  点击:(192)  评论:(0)  加入收藏
在上篇中,我们讨论了C++中与隐式类型转换相关的一些话题,而函数重载是与隐式类型转换相关的又一大重要话题,本篇将要讨论的内容即为隐式类型转换与函数重载之间的相关话题。...【详细内容】
2019-09-06  Tags: C++  点击:(175)  评论:(0)  加入收藏
▌简易百科推荐
一、简介很多时候我们都需要用到一些验证的方法,有时候需要用正则表达式校验数据时,往往需要到网上找很久,结果找到的还不是很符合自己想要的。所以我把自己整理的校验帮助类分...【详细内容】
2021-12-27  中年农码工    Tags:C#   点击:(2)  评论:(0)  加入收藏
引言在学习C语言或者其他编程语言的时候,我们编写的一个程序代码,基本都是在屏幕上打印出 hello world ,开始步入编程世(深)界(坑)的。C 语言版本的 hello world 代码:#include <std...【详细内容】
2021-12-21  一起学嵌入式    Tags:C 语言   点击:(11)  评论:(0)  加入收藏
读取SQLite数据库,就是读取一个路径\\192.168.100.**\position\db.sqlite下的文件<startup useLegacyV2RuntimeActivationPolicy="true"> <supportedRuntime version="v4.0"/...【详细内容】
2021-12-16  今朝我的奋斗    Tags:c#   点击:(21)  评论:(0)  加入收藏
什么是shellshell是c语言编写的程序,它在用户和操作系统之间架起了一座桥梁,用户可以通过这个桥梁访问操作系统内核服务。 它既是一种命令语言,同时也是一种程序设计语言,你可以...【详细内容】
2021-12-16  梦回故里归来    Tags:shell脚本   点击:(18)  评论:(0)  加入收藏
一、编程语言1.根据熟悉的语言,谈谈两种语言的区别?主要浅谈下C/C++和PHP语言的区别:1)PHP弱类型语言,一种脚本语言,对数据的类型不要求过多,较多的应用于Web应用开发,现在好多互...【详细内容】
2021-12-15  linux上的码农    Tags:c/c++   点击:(17)  评论:(0)  加入收藏
1.字符串数组+初始化char s1[]="array"; //字符数组char s2[6]="array"; //数组长度=字符串长度+1,因为字符串末尾会自动添&lsquo;\0&lsquo;printf("%s,%c\n",s1,s2[2]);...【详细内容】
2021-12-08  灯-灯灯    Tags:C语言   点击:(47)  评论:(0)  加入收藏
函数调用约定(Calling Convention),是一个重要的基础概念,用来规定调用者和被调用者是如何传递参数的,既调用者如何将参数按照什么样的规范传递给被调用者。在参数传递中,有两个很...【详细内容】
2021-11-30  小智雅汇    Tags:函数   点击:(19)  评论:(0)  加入收藏
一、问题提出问题:把m个苹果放入n个盘子中,允许有的盘子为空,共有多少种方法?注:5,1,1和1 5 1属同一种方法m,n均小于10二、算法分析设f(m,n) 为m个苹果,n个盘子的放法数目,则先对...【详细内容】
2021-11-17  C语言编程    Tags:C语言   点击:(49)  评论:(0)  加入收藏
一、为什么需要使用内存池在C/C++中我们通常使用malloc,free或new,delete来动态分配内存。一方面,因为这些函数涉及到了系统调用,所以频繁的调用必然会导致程序性能的损耗;另一...【详细内容】
2021-11-17  深度Linux    Tags:C++   点击:(38)  评论:(0)  加入收藏
OpenCV(Open Source Computer Vision Library)是一个(开源免费)发行的跨平台计算机视觉库,可以运行在Linux、Windows、Android、ios等操作系统上,它轻量级而且高效---由一系列...【详细内容】
2021-11-11  zls315    Tags:C#   点击:(50)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条