body, table{font-family: 微软雅黑; font-size: 10pt}
table{border-collapse: collapse; border: solid gray; border-width: 2px 0 2px 0;}
th{border: 1px solid gray; padding: 4px; background-color: #DDD;}
td{border: 1px solid gray; padding: 4px;}
tr:nth-child(2n){background-color: #f8f8f8;}

RTTI、 typeid、类型转换操作符:
——RTTI,是RunTime Type Identification的缩写,称“运行时类型识别”,这是C++中相对较新的特性,一些老式的编译器可能不支持,不同编译器的实现方法也不尽相同。

RTTI机制:

★C++是一种静态类型语言。其数据类型是在编译期就确定的,不能在运行时更改。虚函数的使用使得动态联编成为可能,举例来说,存在一个类层次结构A->B->C,结构定义为;
     class A{…};                //包含虚函数
      class B: public  A{…};
      class C: public  B{…};
★此时,可以直接用派生类(B类或C类)对象为A类指针赋值,而且,通过该A类指针调用虚成员函数时,调用的版本是为其赋值的派生类对此虚函数的覆盖定义。当然,不可能把所有函数都定义成虚函数,对派生类中定义的普通函数来说,使用A类指针调用该函数是否合法需要具体分析。
★先来看如下的强制类型转换机制:
     A *pa = new B;
      B *pb = (B *)pa;        //直接赋值编译器会报错, 需要强制转换
★上述代码是很安全的, 因为pa指向的堆空间中存储的恰好为B类对象,此时使用pb调用B类中定义的非虚函数不会出错。看另一个转换:
    A *pa = new A;
     B *pb = (B *)pa;
★虽然编译器不会报错,但上述代码明显不安全,存在问题,此时使用pb调用B类中的非虚函数必然会出错。


dynamic_cast操作符:
★综合来看,使用派生类对象为基类指针赋值是安全的,可什么时候使用基类指针为派生类指针赋值是安全的呢?C++提供了操作符dynamic_cast,其语法是:
                                          CSon *p1 = dynamic_cast<CSon*>(pBase);
★其中pBase是基类指针,CSon是派生类型,如果pBase指向的对象是CSon型或CSon的派生类型,指针转换成功;否则,p1为null,即空指针。

#include <iostream>
using namespace std;
class A
{
        public:
                virtual void disp()
                {   cout<<"A::disp()"<<endl;   }
                void printA()
                {   cout<<"A::printA()"<<endl;   }
};
class B : public A
{
        public:
                void disp()
                {   cout<<"B::disp()"<<endl;   }
                void printB()
                {   cout<<"B::printB()"<<endl;   }
};
int main()
{
        A *pa = new B;
        B *pb = dynamic_cast<B *>(pa);    //转换安全
        if(pb != NULL)
        {   pb->printB();   }
        else
        {   cout<<"转换不安全,退出"<<endl;   }
        A *p1 = new A;
        B *p2 = dynamic_cast<B *>(p1);    //转换不安全
        if(p2 != NULL)
        {   p2->printB();   }
        else
        {   cout<<"转换不安全,退出"<<endl;   }
        return 0;
}
#include <iostream>
#include <typeinfo>
#include <string.h>
using namespace std;
class Animal //抽象类
{
        public:
                virtual void Say()=0;  //纯虚函数
};
class Dog:public Animal
{
        public:
                void Say()
                {   cout<<"汪汪"<<endl;   }
};
class Cat:public Animal
{
        public:
                void Say()
                {   cout<<"喵喵"<<endl;   }
};
void play(Animal *pa)
{
        if(typeid(*pa) == typeid(Dog) //判断当前指针是不是Dog类型
        {
                Dog * dog = dynamic_cast<Dog*>(pa);
                dog->Say();
        }
        if(typeid(*pa) == typeid(Cat))
        {
                Cat * cat = dynamic_cast<Cat*>(pa);
                cat->Say();
        }
}
int main()
{
        Dog dog;
        Animal *a = &dog;
        cout<<typeid(double).name()<<endl;  //只输出类型第一个字母
        cout<<typeid(5).name()<<endl;
        cout<<"typeid(a).name() "<<typeid(a).name()<<endl; //指针类型
        cout<<"typeid(*a).name() "<<typeid(*a).name()<<endl;
//指针所存的值类型
        play(a);
        Cat cat;
        play(&cat);
}
typeinfo类和typeid操作符:
★在头文件 #include<typeinfo> 中还定义了typeinfo类和typeid操作符,从typeid的字面即可看出,该操作符用以返回类的id,即类型信息,其基本调用格式为: typeinfo&  typeid(expr);    参数expr可以是单个对象,也可以是返回结果为对象的表达式,还可以是类名;返回值是一个typeinfo对象的引用,如果expr是类对象(或类名)、且至少包含有一个虚函数,typeid操作符返回的typeinfo对象需要在运行时计算;否则,返回一个静态对象,在编译时就可以计算得到。
★typeinfo类中包含了一个  name()   成员,返回一个字符串,通常是类名,如下述语句返回的均为静态typeinfo对象:
cout<<typeid(5).name()<<endl;                  //输出字符串“int”
cout<<typeid(double).name()<<endl;        //输出字符串“double”
★来看一个返回动态typeinfo对象的例子:
class A{……};                //包含虚函数
class B:pulic A{……}
A* pa=new B;
cout<<typeid(*pa).name()<<endl;        //输出结果为B
★typeinfo类中对 == != 进行了重载,因此可以使用typeid来判断变量是哪种类型。如语句 if (typeid(x) == typeid(double)) 用来判断变量x的类型是否double类型。


 ●RTTI只能应用于包含虚函数的类层次中,只有在虚函数处理上,使用派生类对象给基类指针赋值才有意义。如果类层次中没有虚函数,将派生类赋值给基类指针没有实质意义。RTTI的引入,可检查基类指针向派生类指针的转换是否安全,为类层次中非虚函数和数据成员的调用提供了方法


类型转换操作符:
★包括dynamic_cast,C++中共添加了4个类型转换符,用以对数据类型的转换进行更严格的限制,分别是dynamic_cast、const_cast、static_cast和reinterpret_cast,和前面所讲的类型转换机制相比,新机制让程序员根据需要选择要使用的操作符,明确了转换意图,可读性更强,而且,编译器可方便地对转换是否安全进行检查,能排查很多传统类型转换无法找出的问题,动态操作符dynamic_cast已在上节介绍过,它将一个指向派生类的基类指针或引用转换为派生类的指针或引用,注意dynamic_cast转换符只能用于含有虚函数的类。

static_cast操作符:
★static_cast的基本语法为:static_cast<T>(expr);     该运算符把expr转换为T型,仅当T类型与expr所属类型能相互隐式转换时,上述转换才合法;否则会报错,能及时发现错误(例如不能把两个不相关的类相互转化)。主要有如下几种用法:
●用于类层次结构中基类和子类之间指针或引用的转换。进行上行转换(把子类的指针或引用转换成基类表示)是安全的;进行下行转换(把基类指针或引用转换成子类表示)时,由于没有动态类型检查,所以是不安全的。
●用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。
●把空指针转换成目标类型的空指针。
●把任何类型的表达式转换成void类型。


const_cast操作符:
★const_cast的基本使用格式为:   const_cast<T>(expr);
★const_cast最常用的用途就是删除const属性,如果某个变量在大多数时候是常量,而在某个时候又是需要修改的,这时就可以使用const_cast操作符了。expr应该为指针和引用的形式,该操作符不会影响expr,除了const和volatile修饰符等,T应与expr类型相同,返回一个新的T型变量,与expr有相同的值。
#include <iostream>
#include <typeinfo>
using namespace std;
int main()
{
        const int i=5;
        const int *pi=&i;
        //*pi = 6;   //不能更改
        cout<<"i= "<<i<<endl;
        cout<<"*pi = "<<*pi<<endl;
        cout<<"pi = "<<pi<<endl;
        int *pi1=const_cast<int *>(pi);     //为什么这样?因为const int *不能初始化int *
//去除pi的const属性,返回新类型变量,不会影响i
        *pi1 = 6;
        //*pi = 6;   //不能更改
        cout<<"i= "<<i<<endl;
        cout<<"*pi = "<<*pi<<endl;
        cout<<"pi = "<<pi<<endl;
        cout<<"*pi1 = "<<*pi1<<endl;
        cout<<"pi1 = "<<pi1<<endl;
        *(const_cast<int *>(pi))=6;  //前面操作可合并为这个
        return 0;
}
#include <iostream>
using namespace std;
int main()
{
        double d = 2.5;
        double *pd = &d;
        int *pi = reinterpret_cast<int *>(pd);
        //int *pi1 = static_cast<int *>(pd);  //error
        //int *pi2 = dynamic_cast<int *>(pd); //error
        int *pi3 = (int *)(pd);
        cout<<"pd= "<<pd<<"\t\tpi= "<<pi<<"\t\tpi3= "<<pi3<<endl;  //因为指针类型不同,所以值不同
        cout<<"*pi= "<<*pi<<"\t\t*pi3= "<<*pi3<<endl;
        int a = 3;   //这里必须int
        int *a1 = reinterpret_cast<int *>(a);        //整形量转换为指针,只能值int类型
        double *a2 = reinterpret_cast<double *>(a);
        cout<<"a1= "<<a1<<"\t\ta2= "<<a2<<endl;
        cout<<"*a1= "<<*a1<<"\t\t*a2= "<<*a2<<endl;   //段错误
        return 0;
}


reinterpret_cast操作符:
★reinterpret_cast的调用格式:    reinterpret_cast<T>(expr)
★reinterpret_cast无法保证转换的安全性,用来将一个类型的指针转变为另一种类型的指针,也用在将整型量转为指针,或将指针转为整型量上。
reinterpret(重新解释)


◆RTTI机制特性能让程序在运行时检测对象的类型,不以指针为转换是否安全的依据,而是考虑指针指向的对象,更本质地说,考虑的是指针指向的内存块的有效性,因而能保证使用指针安全调用虚函数和普通函数。typeid操作符返回一个typeinfo对象的引用,通过typeinfo类提供的name成员函数可以输出参数所属类名。
◆C++中新增的4个类型转换操作符,和传统的类型转换相比,新增的4个cast操作符很好地保证了类型转换的安全性,直观体现了程序员的意图,编译器可很好地进行查错处理。

#include<iostream>
using namespace std;
class point
{
        public:
                point(int x=0,int y=0):_x(x),_y(y){}
                virtual void show()
                {
                        cout<<"("<<_x<<","<<_y;
                }
        private:
                int _x;
                int _y;
};
class point3D:public point
{
        public:
                point3D(int _x=0,int _y=0,int z=0):point(_x,_y),_z(z){}
                virtual void show()
                {
                        point::show();
                        cout<<","<<_z<<")"<<endl;
                }
        private:
                int _z;
};
class String
{
        public:
                String():_mchar(new char[1]){}
                void show()
                {
                        cout<<_mchar<<endl;
                }
        private:
                char* _mchar;
};
int main()
{
        //下面的转换本来是无意义和非法的,以后使用ps->Show()
        //成员函数时可能会引起内存错误或得到错误的值, 但编译却不出错. 留下隐患
        point3D p1(1,2,3);
        String* sp = (String*)&p1;
        sp->show();  // 编译通过,调用直接段错误  // 派生类里面加了point::限定就不会段错误了
        //但改成下面使用static_cast形式进行转换, 在编译时就报错, 能及时发现错误
        //sp = static_cast<String*> (&p1);  // 编译报错
        cout<<"----------------------------------------------------------------"<<endl;
        //而下面这种转换之所以能编译通过,是因为CPoint和CPoint3D的指针本来就可以相互转换
        point* pBase = static_cast<point*> (&p1);
        pBase->show();
        cout<<endl;
        return 0;
}

RTTI,C++类型转换操作符的更多相关文章

  1. C++强制类型转换操作符 dynamic_cast

    dynamic_cast是四个强制类型转换操作符中最特殊的一个,它支持运行时识别指针或引用. >>>>>>>>>>>编译器的RTTI设 ...

  2. C++强制类型转换操作符 const_cast

    const_cast也是一个强制类型转换操作符.<C++ Primer>中是这样描述它的: 1.将转换掉表达式的const性质. 2.只有使用const_cast才能将const性质性质转 ...

  3. C++强制类型转换操作符 static_cast

    static_cast是一个强制类型转换操作符.强制类型转换,也称为显式转换,C++中强制类型转换操作符有static_cast.dynamic_cast.const_cast.reinterpert ...

  4. 类型转换操作符static_cast、const_cast、dynamic_cast、reinterpret_cast

    一.static_cast 对于类型转换,我们常常这么做: (type) expression 引进了static_cast类型转换操作符后,我们只需这样做: static_cast<type& ...

  5. ECMAScript1.1 js书写位置 | 声明变量 | 基本数据类型 | 数据类型转换 | 操作符 | 布尔类型的隐式转换

    js书写位置 由于在写css样式时使用的时双引号,所以我们在写js代码时建议使用单引号(‘’)! 行内式 <input type="button" value="点 ...

  6. C++的四种cast操作符的区别--类型转换(转)

    转自:     http://welfare.cnblogs.com/articles/336091.html Q:什么是C风格转换?什么是static_cast, dynamic_cast 以及 r ...

  7. C++库研究笔记——操作符重载实现类型转换&这样做的意义

    目标: 已知这个接口: std::vector<double> add_vec(double *d1, double *d2) { ..... return result; } 我们自定义 ...

  8. C++的四种cast操作符的区别--类型转换

    Q:什么是C风格转换?什么是static_cast, dynamic_cast 以及 reinterpret_cast?区别是什么?为什么要注意? A:转换的含义是通过改变一个变量的类型为别的类型从而 ...

  9. C++ 类型转换操作与操作符重载 operator type() 与 type operator()

    类型转换操作符(type conversion operator)是一种特殊的类成员函数,它定义将类类型值转变为其他类型值的转换.转换操作符在类定义体内声明,在保留字 operator 之后跟着转换的 ...

随机推荐

  1. Python&HDF5目录

    最近一直没更新python&量化的博客,是因为忙于看HDF5的书,写VNPY框架,学scrapy爬虫. 本来写博客的目的就是为了当作一种教材,当遇到不会的问题过来找答案. 对于HDF5下面这本 ...

  2. (转+整理)C#中动态执行代码

    通过微软提供的CSharpCodeProvider,CompilerParameters,CompilerResults等类,可以在运行时,动态执行自己写的代码文件.原理就是把你的代码文件动态编译成e ...

  3. 为 昂达 v891 安装上了 remix OS 了

    起因: 默认的ROM自带一堆垃圾app,最主要的是没有root , 所以卸载不了. 然后试了 Root大师 , 刷机精灵 之类的软件. 我 CTMD , 简直比出厂ROM 还流氓, 不断的强制安装各种 ...

  4. 数据结构(C语言版)-第8章 排序

    8.1 概述 1. 什么是排序?  将一组杂乱无章的数据按一定规律顺次排列起来. 2. 排序的目的是什么? ——便于查找! 3. 什么叫内部排序?什么叫外部排序? 若待排序记录都在内存中,称为内部排序 ...

  5. 记一次RAID阵列的迁移经历

    xu言: 最近,某电信机房因为空调漏水问题导致了我司的Dell R430 服务器的主板及CPU不同程度受损.服务器已经不能正常开机.但是,又基于把服务器的数据需要最短时间进行恢复.抱着试试看的心里进行 ...

  6. LeetCode--263--丑数

    问题描述: 编写一个程序判断给定的数是否为丑数. 丑数就是只包含质因数 2, 3, 5 的正整数. 示例 1: 输入: 6 输出: true 解释: 6 = 2 × 3 示例 2: 输入: 8 输出: ...

  7. python基础之 反射,md5加密 以及isinstance, type, issubclass内置方法的运用

    内容梗概: 1. isinstance, type, issubclass 2. 区分函数和方法 3. 反射(重点) 4. md5加密 1. isinstance, type, issubclass1 ...

  8. ZOJ - 3661 pam

    题意:给一个字符串,和每个字符代表的val,每个回文串的价值就是前半部分的val26进制%777777777,求价值第k小的回文串 题解:建个pam,然后dfs两边(0,1),统计价值sort一遍就好 ...

  9. python-django rest framework框架之分页

    1. 以前django做的分页组件当数据量特别大的时候,性能不是很高,有以下三种方式处理:        a. 记录当前访问页的最后一条数据id,往后取多少条        b. 最多显示120页   ...

  10. 使用formData上传文件,ajax上传

    项目是vue项目,直接贴部分代码了: html: <input type="file" name="fileTable" @change="ch ...