RTTI,C++类型转换操作符

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;

}


原文地址:https://www.cnblogs.com/meihao1203/p/9368287.html

时间: 2024-08-26 19:13:58

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

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

dynamic_cast是四个强制类型转换操作符中最特殊的一个,它支持运行时识别指针或引用. >>>>>>>>>>>编译器的RTTI设置>>>>>>>>>>> dynamic_cast提供RTTI(Run-Time Type Information),也就是运行时类型识别.它对编译器有要求,需要编译器启动“运行时类型信息”这一选项.当编译器不开启RTTI时,运行含有dynam

C++ 四种类型转换操作符

一.C风格类型转换操作符 (type) expression 例子: int firstNumber, secondNumber; double result = ((double)firstNumber)/secondNumber: 二.C++类型转换操作符 1.static_cast  在功能上基本上与 C 风格的类型转换一样强大,含义也一样. double result = static_cast<double>(firstNumber)/secondNumber; 它也有功能上限制.例

c++ cast operator(类型转换操作符)

复制于:http://xiaochonganty.blog.163.com/blog/static/48527932008931104132748/ New Cast Operators Originally, the C++ standardization committee wanted to deprecate C-style casting, thereby enforcing the use of the new cast operators exclusively. However,

c++四种类型转换操作符简要总结

1.  static_cast<target>(expression) 可用于存在继承关系的类指针/引用之间的向上向下转换.执行向下转换时没有安全检查(区别于dynamic_cast),所以如果实际类型与目标类型不符合,编译虽然可以通过,但运行时的后果未定义. void*指针转换为实际的指针 其他基本类型的隐式转换以及反方向的转换,如:int <-> double 2.  dynamic_cast<target>(expression) 只能用于target为类指针或者

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

const_cast也是一个强制类型转换操作符.<C++ Primer>中是这样描述它的: 1.将转换掉表达式的const性质. 2.只有使用const_cast才能将const性质性质转化掉.试图使用其他三种形式的强制转换都会导致编译时的错误.(添加const还可以用其他转换符,如static_const) 3.除了添加const或删除const特性,使用const_cast符来执行其他任何类型的转换都会引起编译错误.(volatile限定符也包括,不过我不怎么了解,本文主要说const)

RTTI和类型转换

RTTI 通过运行时类型识别(RTTI),程序能够使用基类的指针或引用来检索这些指针或引用所指对象的实际派生类型.通过下面两个操作符提供 RTTI: 1. typeid 操作符,返回指针或引用所指对象的实际类型. 2. dynamic_cast 操作符,将基类类型的指针或引用安全地转换为派生类型的指针或引用. 这些操作符只为带有一个或多个虚函数的类返回动态类型信息,对于其他类型,返回静态(即编译时)类型的信息.对于带虚函数的类,在运行时执行 RTTI 操作符,但对于其他类型,在编译 时计算 RT

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

类型转换操作符(type conversion operator)是一种特殊的类成员函数,它定义将类类型值转变为其他类型值的转换.转换操作符在类定义体内声明,在保留字 operator 之后跟着转换的目标类型.class CVImage{public :    CVImage();    explicit CVImage(unsigned int width, unsigned int height, unsigned short depth, unsigned short nChannels

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

Q:什么是C风格转换?什么是static_cast, dynamic_cast 以及 reinterpret_cast?区别是什么?为什么要注意? A:转换的含义是通过改变一个变量的类型为别的类型从而改变该变量的表示方式.为了类型转换一个简单对象为另一个对象你会使用传统的类型转换操作符.比如,为了转换一个类型为doubole的浮点数的指针到整型:代码:int i;double d; i = (int) d;或者: i = int (d); 对于具有标准定义转换的简单类型而言工作的很好.然而,这样

C++ 宏、范型和RTTI 浅析

[摘要] RTTI(Run-Time Type Identification)是面向对象程序设计中一种重要的技术.现行的C++标准对RTTI已经有了明确的支持.不过在某些情况下出于特殊的开发需要,我们需要自己编码来实现.本文介绍了一些关于RTTI的基础知识及其原理和实现,并分析比较三者是线上的差异与联系. [正文] RTTI 的需求 和很多其他语言一样,C++是一种静态类型语言.其数据类型是在编译期就确定的,不能在运行时更改.然而由于面向对象程序设计中多态性的要求,C++中的指针或引用(Refe