C++的创始人认为C语言的类型转换运算符太过于松散。他采取了更加严格的限制允许的类型转换。并添加了4个类型转换运算符。

这部分特性比较高阶,我把它归于奇技淫巧的范畴。这里简单介绍一下,以后实际有用到再深入研究。

struct Data

{

  double data[200];

};

struct Junk

{

  int junk[100];

};

Data d = {2.5e33, 3.5e-19, 20.2e32};

char * pch = (char *) (&d);    //type cast #1

char ch = char (&d);              //type cast #2

Junk * pj = (Junk *) (&d);       //type cast #3

首先,上述3种类型转换中,哪一种更有意义?除非不讲理,否则它们中没有一个是有意义的。其次,这3种类型转换中哪种是允许的呢?在C语言中都是允许的。

对于这种松散的情况,Stroustrop采取的措施是,更严格地限制允许的类型转换,并添加4个类型转换运算符,使转换过程更加规范:

dynamic_cast;

const_cast;

static_cast;

reinterpret_cast;

可以根据目的选择一个合适的运算符,而不是使用通用的类型转换。

这指出了进行类型转换的原因,并让编译器能够检查程序的行为是否与设计者想法吻合。

======================================================

举个例子dynamic_cast运算符在前面已经介绍过了,假设High和Low是两个类,而ph和pl的类型分别为High *和Low *。

则仅当Low是High的可访问基类时,下面的语句才将一个Low*指针赋给pl:

pl = dynamic_cast<Low *> ph;

否则,该语句将空指针赋给pl。

该运算符的语法如下:

dynamic_cast <type_name> (expression)

该运算符的用途是,使得能够在类层次结构中进行向上转换(由于是is-a关系,这种转换是安全的),而不允许其他转换。

======================================================

const运算符用于执行只有一种用途的类型转换。

即改变值为const或volatile,其语法与dynamic_cast运算符相同:

const_cast <type_name>(expression)

如果类型的其他方面也被修改,则上述类型的转换将出错。

也就是说除了const和volatile特征可以不同外,type_name和expression的类型必须相同。

再次假设High和Low是两个类:

High bar;

const High * pbar = &bar;

...

High * pb = const_cast<High *> (pbar);                     //valid

const  Low * pl = const_cast<const Low *> (pbar);   //invalid

第一个类型转换使得*pb成为一个可用于修改bar对象值的指针,它删除const标签。

第二个类型转换是非法的,因为它同时尝试将类型从const High*改为const Low*。

提供该运算符的原因是,有时候可能需要这样一个值,它在大多数时候是常量,而有时又是可修改的。

在这种情况下,可以将这个值声明为const,并在需要修改它的时候,使用const_cast。

这也可以通过通过类型转换实现,但通用转换也可能同时改变类型:

High bar;

const High * pbar = &bar;

...

High * pb =(High *)(pbar);

Low * pl = (Low *) (pbar);

由于编程时可能无意间同时改变类型和常量特征,因此使用const_cast运算符更安全;

const_cast不是万能的。它可以修改指向一个值的值,但修改const的值的结果是不确定的。

 //constcast.cpp  -- using const_cast<>
#include <iostream>
using std::cout;
using std::endl; void change(const int * pt, int n); int main()
{
int popl = ;
const int pop2 = ; cout<<"pop1, pop2:"<<pop1<<", "<<pop2<<endl;
change(&pop1, -);
change(&pop2, -);
cout<<"pop1, pop2:"<<pop1<<", "<<pop2<<endl;
return ;
} void change(const int * pt, int n)
{
int * pc;
pc = const_cast<int *>(pt);
*pc += n;
}

const_cast运算符可以删除const int *中的const,使得编译器能够接受change()中的语句:

*pc += n;

但由于pop2被声明为const,因此编译器可能禁止修改它,如下面的输出所示:

pop1, pop2 : 38383, 2000

pop1, pop2 : 38280, 2000

正如您所看到的,调用change()时,修改了pop1,但没有修改pop2.。

在change()中,指针被声明为const int * ,因此不能用来修改指向的int。

指针pc删除了const特征,因此可用来修改指向的值,但仅当指向的值不是const时才行。

因此,pc可用于修改pop1,但不能用于修改pop2。

======================================================

static_cast运算符的语法与其他类型转换运算符相同:

static_cast <type-name> (expression)

仅当type_name可被隐式转换为expression所属的类型或expression可被隐式转换为type_name所属的类型时,上述转换才是合法的,否则将会出错。

假设High是Low的基类,而Pond是一个无关的类,则从High到Low的转换,从Low到High的转换都是合法的,而从Low到Pond的转换时不允许的。

High bar;

Low blow;

...

High * pb = static_cast<High *> (&blow);    //valid upcast

Low * pl = static_cast<Low *>(&bar);    //valid downcast

Pond * pmer = static_cast<Pond *>(&blow);   //invalid, Pond unrelated

第一种转换是合法的,因为向上转换可以显式地进行。

第二种转换是从基类指针到派生类指针,在不进行显式转换的情况下,将无法进行。但由于无需进行类型转换,便可以进行另一个方向的类型转换,因此使用static_cast进行向下转换是合法的。

同理,由于无需进行类型转换,枚举值就可以被转换为整型,所以可以用static_cast将整型转换为枚举值。同样,可以使用static_cast将double转换为int、将float转换为long以及其他各种数值转换。

//完全被绕晕了

======================================================

reinterpret_cast运算符用于天生危险的类型转换。它不允许删除const,但会执行其他令人生厌的操作。

有时程序员必须做一些依赖于实现的、令人生厌的操作,使用reinterpret_cast运算符可以简化对这种行为的跟踪工作。该运算符的语法与另外3个相同:

reinterpret_cast <type-name> (expression)

下面是一个使用示例:

struct dat { short a; short b;}

long value = 0xA224B118;

dat * pd = reinterpret_cast<dat *> (&value);

cout << hex << pd->a;  //display first 2 bytes of value

通常,这样的转换适用于依赖于实现的底层编程技术,是不可移植的。

例如,不同系统在存储多字节整型时,可能以不同的顺序存储其中的字节。

然而,reinterpret_cast运算符并不支持所有的类型转换。例如,可以将指针类型转换为足以存储指针表示的整型,但不能将指针转换为更小的整型或浮点型。

另一个限制是,不能将函数指针转换为数据指针,反之亦然。

  在C++中,普通类型转换也受到限制。基本上,可以执行其他类型转换可执行的操作,加上一些组合,如static_cast或reintepret_cast后跟const_cast,但不能执行其他转换。

因此,下面的类型转换在C语言中是允许的,但在C++中通常不允许,因为对于多数C++shixian ,char类型都太小,不能存储指针。

char ch =char (&d)    //type cast #2 - convert address to a char

这些限制是合理的,如果您觉得这种限制难以忍受,可以使用C语言。

//这段也是晦涩难懂

C++_新特性1-类型转换运算符的更多相关文章

  1. C++_新特性2-RTTI运行阶段类型识别

    这部分属于C++的新特性,感觉比较高阶的特性.我把它归于属于奇技淫巧的范畴.了解即可. RTTI是运行阶段类型识别(Runtime Type Identification)的简称. 这是添加到C++中 ...

  2. javascript ES6 新特性之 扩展运算符 三个点 ...

    对于 ES6 新特性中的 ... 可以简单的理解为下面一句话就可以了: 对象中的扩展运算符(...)用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中. 作用类似于 Object.assign() ...

  3. C++_新特性总结与未来的路

    了解C++之后,可以阅读一些高级主题和面向对象编程相关的书籍: OOP有助于开发大型的项目,并提高其可靠性: OOP方法的基本活动之一就是发明能够模拟当前情况的类.当前情况被统称为问题域. 由于实际问 ...

  4. java基础学习总结三(jdk7新特性、变量(局部变量和成员变量)、常量以及运算符)

    一:jdk7新特性 可以表示二进制数值,以0b开头,中间可以使用下划线_分隔符.如下: @Test /** * 测试jdk新特性 */ public void testJdk7(){ int a=0b ...

  5. JavaSE学习总结第27天_反射 & 设计模式 & JDK5、7、8新特性

      27.01  反射_类的加载概述和加载时机 类的加载:当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化. 加载:就是指将class文件读 ...

  6. C# 新特性_协变与逆变 (.net 4.0)

    C#4.0中有一个新特性:协变与逆变.可能很多人在开发过程中不常用到,但是深入的了解他们,肯定是有好处的. 协变和逆变体现在泛型的接口和委托上面,也就是对泛型参数的声明,可以声明为协变,或者逆变.什么 ...

  7. 【Servlet3.0新特性】第03节_文件上传

    这是一个Web Project 首先是web.xml <?xml version="1.0" encoding="UTF-8"?> <web- ...

  8. HTML5基础知识汇总_(2)自己定义属性及表单新特性

    自己定义属性data-* 说起这个属性,事实上如今非经常见了;怎么说呢,由于在一些框架都能看到他的身影!!! 比方Jquery mobile,里面非常频繁的使用了这个属性; 这个属性是哪里来的-.当然 ...

  9. JS魔法堂:ES6新特性——GeneratorFunction介绍

    一.前言       第一次看koajs的示例时,发现该语句 function *(next){...............} ,这是啥啊?于是搜索一下,原来这是就是ES6的新特性Generator ...

随机推荐

  1. libevent源码深度剖析九

    libevent源码深度剖析九 ——集成定时器事件 张亮 现在再来详细分析libevent中I/O事件和Timer事件的集成,与Signal相比,Timer事件的集成会直观和简单很多.Libevent ...

  2. libevent源码深度剖析四

    libevent源码深度剖析四 ——libevent源代码文件组织 1 前言 详细分析源代码之前,如果能对其代码文件的基本结构有个大概的认识和分类,对于代码的分析将是大有裨益的.本节内容不多,我想并不 ...

  3. vmware 安装不成功导致的问题解决以及右键菜单添加打开终端命令

    转自http://blog.csdn.net/puweilan/article/details/8609952 在VMware安装Ubuntu完成后,一直停留在VMware Easy Install, ...

  4. PHP加密与解密

    password_hash ( string $password , integer $algo [, array $options ] ) 加密,生成60位得字符串 $algo:一个用来在散列密码时 ...

  5. PHP中 null ,false , 区别

    先来测试一下吧: if(0 ==''){ echo '<br/>在PHP中0 ==\'\'' ; } if(0 !==''){ echo '<br/>在PHP中0 !==\'\ ...

  6. javax.servlet.jsp.PageContext cannot be resolved to a type

    <dependency> <groupId>javax.servlet</groupId> <artifactId>jsp-api</artifa ...

  7. python 爬虫proxy,BeautifulSoup+requests+mysql 爬取样例

    实现思路: 由于反扒机制,所以需要做代理切换,去爬取,内容通过BeautifulSoup去解析,最后入mysql库 1.在西刺免费代理网获取代理ip,并自我检测是否可用 2.根据获取的可用代理ip去发 ...

  8. 在iis7.5上部署asp.net mvc5

    部署mvc5跟部署mvc4是一样的,唯一不同的是需要修改一下web.config的配置 在web.config中加入一下节点即可 <system.webServer> <module ...

  9. C# 多态(2)

    接上面讲 我们知道虚方法,和重写方法. 但是有没有发现 override和new的作用是差不多的. 但为什么还要单独写出来两个呢? 首先,咱们要明白一个问题,继承是具有线性传播的. class Fat ...

  10. ubuntu没有权限(不能)创建文件夹(目录)

    可以在终端直接运行 sudo nautilus,弹出来的nautilus可以直接GUI操作,中途别关终端.如果遇到需要输入root密码,则输入root密码就可以启动这个图形界面了.