新风格类型转换和运行时类型信息(RTTI)
- 格式:docx
- 大小:46.42 KB
- 文档页数:12
C++四种类型转换总结C风格的强制类型转换很简单,均⽤ Type b = (Type)a 形式转换。
C++风格的类型转换提供了4种类型转换操作符来应对不同场合的应⽤,如下表:转换类型操作符作⽤const_cast去掉类型的const或volatile属性static_cast⽆条件转换,静态类型转换dynamic_cast有条件转换,动态类型转换,运⾏时检查类型安全(转换失败返回NULL)reinterpret_cast仅重新解释类型,但没有进⾏⼆进制的转换1、const_cast去掉类型的const或volatile属性int main() {struct T {int i;};const T a;//a.i = 10; //直接修改const类型,编译错误T &b = const_cast<T&>(a);b.i = 10;return0;}2、static_cast类似C风格的强制转换,进⾏⽆条件转换,静态类型转换:1)基类和⼦类之间的转换:其中⼦类指针转换为⽗类指针是安全的,但⽗类指针转换为⼦类指针是不安全的(基类和⼦类之间的动态类型转换建议⽤dynamic_cast)。
2)基本数据类型转换,enum,struct,int,char,float等。
static_cast不能进⾏⽆关类型(如⾮基类和⼦类)指针之间的转换。
3)把任何类型的表达式转换成void类型。
4)static_cast不能去掉类型的const、volatile属性(⽤const_cast)。
int main() {int n = 6;double d = static_cast<double>(n); //基本类型转换int *pn = &n;double *d = static_cast<double*>(&n); //⽆关类型转换,编译错误void *p = static_cast<void*>(pn);return0;}3、dynamic_cast有条件转换,动态类型转换,运⾏时检查类型安全(转换失败返回NULL):1)安全的基类和⼦类之间的转换。
c++RTTI(运⾏时类型识别)通过RTTI,能够通过基类的指针或引⽤来检索其所指对象的实际类型。
c++通过下⾯两个操作符提供RTTI。
(1)typeid:返回指针或引⽤所指对象的实际类型。
(2)dynamic_cast:将基类类型的指针或引⽤安全的转换为派⽣类型的指针或引⽤。
对于带虚函数的类,在运⾏时执⾏RTTI操作符,返回动态类型信息;对于其他类型,在编译时执⾏RTTI,返回静态类型信息。
当具有基类的指针或引⽤,但需要执⾏派⽣类操作时,需要动态的强制类型转换(dynamic_cast)。
这种机制的使⽤容易出错,最好以虚函数机制代替之。
dynamic_cast 操作符如果dynamic_cast转换指针类型失败,则返回0;如果转换引⽤类型失败,则抛出⼀个bad_cast类型的异常。
可以对值为0的指针使⽤dynamic_cast,结果为0。
dynamic_cast会⾸先验证转换是否有效,只有转换有效,操作符才进⾏实际的转换。
if (Derived *derivedPtr = dynamic_cast<Derived *>(basePtr)){// use the Derived object to which derivedPtr points}else{ // basePtr points at a Base object// use the Base object to which basePtr points}也可以使⽤dynamic_cast将基类引⽤转换为派⽣类引⽤:dynamic_cast<Type&>(val)因为不存在空引⽤,所以不能像指针⼀样对转换结果进⾏判断。
不过转换引⽤类型失败时,会抛出std::bad_cast异常。
try{const Derived &d = dynamic_cast<const Derived&>(b);}catch (bad_cast) {// handle the fact that the cast failed.}typeid操作符typeid能够获取⼀个表达式的类型:typeid(e)。
Java类型信息RTTI——运⾏时类型信息(Run-Time Type Information),通过运⾏时类型信息程序能够使⽤的或引⽤来检查这些指针或引⽤所指的对象的实际型。
RTTI提供了以下两个⾮常有⽤的操作符:(1)typeId操作符,返回和引⽤所指的实际类型。
(2)操作符,将类型的指针或引⽤安全地转换为型的指针或引⽤。
代码⽰例:1public class Test1 {2public static void main(String[] args) {34// 把Square,Triangle对象放⼊List时会向上转型为Shape5 List<Shape> shapelist = Arrays.asList(new Shape(), new Square(), new Triangle());67// 多态机制确保:Shape对象执⾏什么样的代码由引⽤所指向的具体对象确定的9for (Shape shape : shapelist) {10 shape.draw();11 }12 }13 }运⾏结果:我是基类Shape的draw()⽅法!我是⼦类Square的draw()⽅法!我是⼦类Triangle的draw()⽅法!总结:使⽤RTTI,可以查询某个Shape引⽤所指向的对象的确切类型,然后选择或者剔除个例。
注意:RTTI允许通过匿名基类的引⽤来发现类信息,但是要注意能够使⽤多态机制实现的话,优先选⽤多态,否则在代码开发以及维护过程中损失了很多多态机制的重要价值,必要的时候才会选⽤RTTI。
RTTI在java中的⼯作原理:在java中有⼀种称为Class对象的特殊对象,其包含与类有关的信息,RTTI即是通过Class对象来执⾏的,每当编写完了⼀个新类,然后进⾏编译,java虚拟机的“类加载器”⼦系统在运⾏程序时,在程序创建第⼀个对类的静态成员的引⽤时,就会加载这个类,(说明:即使在构造器前没有static关键字修饰,类的构造器也是类的静态⽅法,因此使⽤new操作符创建类对象时也可以看做是对类的静态成员的引⽤)。
c++类型转换规则
C++中有几种类型转换规则,它们决定了不同类型之间的转换方式。
以下是主要的类型转换规则:
1. 隐式类型转换(Implicit Type Conversion):
隐式类型转换是由编译器自动完成的,它在需要的时候将一种数据类型转换为另一种数据类型。
例如,将一个整数赋值给一个浮点数变量时,会发生隐式类型转换。
2. 显式类型转换(Explicit Type Conversion):
显式类型转换是由程序员显式地指定的,也被称为强制类型转换。
在某些情况下,需要确保数据以特定的类型进行转换。
显式类型转换使用特定的 C++ 运算符来实现。
以下是四种显式类型转换的方式:
- 静态转换(static_cast):通常用于非多态类型的转换。
- 动态转换(dynamic_cast):通常用于基类和派生类之间的转换,进行运行时类型检查。
- 重新解释转换(reinterpret_cast):用于位级别的类型转换,比较危险,慎用。
- 常量转换(const_cast):用于添加或移除类型的 const 属性。
3. C 风格类型转换(C-style Type Casting):
C++兼容C语言的类型转换方式,使用圆括号并在括号中指定要转换的目标类型。
它与静态转换类似,但语法上不太安全,可能会导致错误,因此在C++中更推荐使用静态转换。
请注意,类型转换可能导致数据丢失或溢出,因此在进行类型转换时要格外小心,确保转换的正确性和安全性。
在使用类型转换时,
应该优先考虑使用更安全的静态转换,并避免使用危险的重新解释转换。
C++语言程序设计第六章RTTI本章主要内容z四个强制转换运算符z RTTI的概念z RTTI的两种使用方法合理使用z RTTIC ++新增四个强制类型转换符“强制”的含义:在告诉编译器:“我知道你不愿意这样做,可是你必须做。
尽管执行吧,后果由我负责!”结果是编译器放弃了类型检查,于是程序得以顺利通过编译,但隐患得以过关,后果要由程序员承担。
“强制”意味着责任。
第一个强制类型转换符const castz const_cast除去对象的常属性。
转换的是表达式而非对象自身.形式:const_cast type ( object )const cast<type>(object)const cast<>string const B(“Hello world !”);使用const_cast<>的例:string const B(“Hello world !”);// 创建了一个常对象string & alsoB= const_cast<string &>(B);//alsoB // alsoB是B 的别名,是个非常对象!显然是隐患。
(参见第五讲多态《一个可怕的例子:》)const cast<T*>(p)//const p const_cast<T >(p)// 把const p 变成非const 的;const_cast<const T*>(p)// 把p 变成const 类型的;p 的原本静态类型必须是T 类型的指针;第二个强制类型转换符static castz static_cast用来进行非多态的任何转换。
拒绝了运行时的类型检查。
形式:static_cast type ( object ) 。
static cast<type>(object)是个杂物袋,不属于其它三种转换的转换都归它,最像C风格的( ) 转换。
Google有很多自己实现的使C++代码更加健壮的技巧、功能,以及有异于别处的C++的使用方式。
1. 智能指针(Smart Pointers)如果确实需要使用智能指针的话,scoped_ptr完全可以胜任。
在非常特殊的情况下,例如对STL容器中对象,你应该只使用std::tr1::shared_ptr,任何情况下都不要使用auto_ptr。
“智能”指针看上去是指针,其实是附加了语义的对象。
以scoped_ptr为例,scoped_ptr被销毁时,删除了它所指向的对象。
shared_ptr也是如此,而且,shared_ptr实现了引用计数(reference-counting),从而只有当它所指向的最后一个对象被销毁时,指针才会被删除。
一般来说,我们倾向于设计对象隶属明确的代码,最明确的对象隶属是根本不使用指针,直接将对象作为一个域(field)或局部变量使用。
另一种极端是引用计数指针不属于任何对象,这样设计的问题是容易导致循环引用或其他导致对象无法删除的诡异条件,而且在每一次拷贝或赋值时连原子操作都会很慢。
虽然不推荐这么做,但有些时候,引用计数指针是最简单有效的解决方案。
译者注:看来,Google所谓的不同之处,在于尽量避免使用智能指针:D,使用时也尽量局部化,并且,安全第一。
其他C++特性1. 引用参数(Reference Arguments)所以按引用传递的参数必须加上const。
定义:在C语言中,如果函数需要修改变量的值,形参(parameter)必须为指针,如int foo(int *pval)。
在C++中,函数还可以声明引用形参:int foo(int &val)。
优点:定义形参为引用避免了像(*pval)++这样丑陋的代码,像拷贝构造函数这样的应用也是必需的,而且不像指针那样不接受空指针NULL。
缺点:容易引起误解,因为引用在语法上是值却拥有指针的语义。
结论:函数形参表中,所有引用必须是const:void Foo(const string &in, string *out);事实上这是一个硬性约定:输入参数为值或常数引用,输出参数为指针;输入参数可以是常数指针,但不能使用非常数引用形参。
rtti原理
RTTI原理是指运行时类型识别(Run-Time Type Identification)的原理。
在C++中,RTTI是一种机制,它允许程序在运行时确定一个对象的类型。
这个机制是通过在对象中存储类型信息来实现的。
RTTI机制在C++中非常重要,因为它允许程序在运行时进行类型检查和类型转换。
RTTI机制的实现是通过两个关键字来实现的:typeid和dynamic_cast。
typeid关键字用于获取一个对象的类型信息,而dynamic_cast关键字用于将一个对象转换为另一个类型的对象。
这两个关键字都是在运行时执行的,因此它们被称为运行时类型识别。
RTTI机制的实现需要在对象中存储类型信息。
在C++中,每个类都有一个虚函数表(Virtual Function Table),这个表中存储了类的虚函数的地址。
在这个表的开头,通常会有一个指向类的类型信息的指针。
这个指针指向一个类型信息对象,这个对象包含了类的名称、大小、父类信息等等。
RTTI机制的实现还需要注意一些细节。
首先,RTTI机制只能用于具有虚函数的类。
其次,RTTI机制的实现可能会影响程序的性能,因为它需要在运行时进行类型检查和类型转换。
因此,在使用RTTI 机制时,需要权衡程序的性能和代码的可读性。
RTTI机制是C++中非常重要的一个机制,它允许程序在运行时进
行类型检查和类型转换。
RTTI机制的实现需要在对象中存储类型信息,并且只能用于具有虚函数的类。
在使用RTTI机制时,需要权衡程序的性能和代码的可读性。
DelphiRTTI资料一:获得对象的RTTI(以下代码基于Delphi 6/7)RTTI(Runtime Type Information 运行时类型信息)指的是对象在编译的时候,将其属性、方法的类型信息编译进特殊的区域,使得应用程序可以运行时,动态的查找对象的成员(fields)和属性(properties),以及定位函数(methods)。
能够编译进RTTI的成员主要是被声明在对象的published部分,对象published部分的成员与public部分的成员具有相同的可见性,但区别就在于此。
当在持久化和反持久化的时候,RTTI被用来访问属性的值、在对象浏览器(Object Inspector)中显示属性,以及关联事件(Event)和事件句柄函数(Event Handler)。
Published部分的属性类型被限定在几种特殊的类型中,只能是Ordinal(有序类型)、string、class、interface、variant和函数指针类型,数组属性是不能被声明为published。
当然也不是每一个被声明在published部分的成员都可以被持久化下来,比如record、array,同时声明在published部分的函数不支持overload。
任何一个类希望拥有RTTI,需要在其类声明的时候加上编译开关{$M+},或者其父类在声明的时候有{$M+},所以最简单的方式获得RTTI就是从TPersistent继承。
对象属性的RTTI特别注意,并不是所有类型的属性都可以被编译到RTTI中。
以下是获得属性、类型的方法function GetObjProps(AObj: TPersistent): String;varStrList: TStringList;PropCount, I: Integer;PropList: PPropList;beginStrList:= TStringList.Create;tryPropCount:= GetPropList(AObj, PropList);tryif PropCount>0 thenfor I := 0 to PropCount-1 doStrList.Append(Format('Property %s : %s ;',[PropList[I]^.Nam e, PropList[I]^.PropType^^.Name]));finallyif PropCount>0 then FreeMem(PropList,PropCount*SizeOf(Pointer));end;Result:= StrList.T ext;finallyStrList.Free;end;end;当自己制作一个属性浏览器的时候,就可以通过TypInfo单元中的各种方法,获得属性名称、类型、值的读写。
新风格类型转换和运行时类型信息(RTTI)C++中有4种新风格类型转换操作符。
每一种操作符都返回一个根据操作符规则转换的对象。
它们使用以下语法:cast_operator<type>(object)cast_operator(类型转换操作符)是下面4种之一:dynamic_cast(动态类型转换)、static_cast(静态类型转换)、reinterpret_cast(重新解释类型转换)和const_cast(常类型转换)。
Type参数是对象要转换为的类型,object参数是要进行类型转换的对象。
1.1dynamic_castdynamic_cast操作符将一个基类引用或指针转换为一个派生类引用或指针,或者将一个派生类引用或指针转换为一个基类引用或指针。
只有当基类至少有一个虚函数时,才可以使用dynamic_cast。
dynamic_cast操作符允许程序决定运行时一个基类引用或指针是否可以指向一个特定派生类的对象或派生于该特定类的类的对象(将一个基类对象指针(或引用)dynamic_cast到派生类指针,dynamic_cast会根据基类指针是否真正指向派生类指针来做相应处理,即会作一定的判断。
)。
这种操作称为向下类型转换。
对于加粗字的理解有下面一个例子:/*对指针进行dynamic_cast,失败返回null,成功返回正常cast后的对象指针;对引用进行dynamic_cast,失败抛出一个异常,成功返回正常cast后的对象引用。
*/#include <iostream>using namespace std;class CBasic{public:virtual int test(){return 0;} // 一定要是virtual};class CDerived : public CBasic{public:virtual int test(){return 1;}};class CDerived2 : public CDerived{public:virtual int test(){return 2;}};int main(){CBasic cBasic;CDerived cDerived;CDerived2cDerived2;CBasic * pB1 = new CBasic;//基类指针CBasic * pB2 = new CDerived;//基类指针指向继承类指针CBasic * pB3 = new CDerived2;//基类指针指向间接继承类指针CDerived * pD1 = dynamic_cast<CDerived * > (pB1); //dynamic cast失败, 所以pD1是null.CDerived * pD2 = dynamic_cast<CDerived * > (pB2); //dynamic cast成功, 所以pD2指向CDerived对象CDerived * pD3 = dynamic_cast<CDerived * > (pB3); //dynamic cast成功, 所以pD3指向CDerived对象/*try{CDerived & rD1 = dynamic_cast<CDerived &> (*pB1); //dynamci cast失败,所以抛出异常.}catch (bad_cast){std::cout << "nonTextBox, can't paint" << std::endl;}*/CDerived & rD2 = dynamic_cast<CDerived &> (*pB2); //dynamic cast成功,所以rD2为CDerived对象的引用.return 0;}1.1.1对指针进行向下类型转换如果对于一个指针的dynamic_cast操作是无效的,例如,被转换的指针类型不是被转换的类层次的成员,那么dynamic_cast将返回零值。
清单1.1是用dynamic_cast对指针进行向下类型转换的一个示例。
注:在使用RTTI前要进行“工程→设置→C/C++→分类→C++语言→允许时间类型信息(RTTI)”,否则会出错。
////////////////////////////////////////// File Name: pr27001.cpp////////////////////////////////////////#include <typeinfo>#include <iostream>////////////////////////////////////////// Define the Shape class.////////////////////////////////////////class Shape{public:virtual void foo() {} // To enable rtti.};////////////////////////////////////////// Declare classes derived from Shape.////////////////////////////////////////class Circle: public Shape { };class Rectangle : public Shape { };////////////////////////////////////////// Process Circle and Rectangle objects.////////////////////////////////////////void Process(Shape* sp){// Downcast Shape* to Circle*.Circle* cp = dynamic_cast<Circle*>(sp);if (cp != 0){std::cout << "Processing a Circle" << std::endl;return;}// Downcast Shape* to Rectangle*.Rectangle* rp = dynamic_cast<Rectangle*>(sp);if (rp != 0){std::cout << "Processing a Rectangle" << std::endl;return;}std::cout << "Unknown Shape, cannot process" << std::endl; }////////////////////////////////////////// The main() function.////////////////////////////////////////int main(){// Instantiate and process a Circle.Circle circle;Process(&circle);// Instantiate and process a Rectangle.Rectangle rect;Process(&rect);// Instantiate and process a generic Shape.Shape shape;Process(&shape);return 0;}清单1.1中的Process()函数知道从Shape基类派生的对象。
Process()是一个非成员函数,不是Shape类的成员,Shape类不需要知道它自已现有的和将来的所有实例。
这种方式是对使用被派生类重写的纯虚函数或空函数Shape::Process()的一个替代。
Process()代表一些没有实现任何特定抽象数据类型行为的外部一般过程,它需要使用对象类的接口。
如果地址传递给Process()的对象不是Circle,那么dynamic_cast<Circle*>操作将返回零值,并且Process()知道不需要调用对于Circle惟一的函数;与此类似,若对象不是Rectangle,那么dynamic_cast<Rectangle*>操作返回零值,Process()同样知道不需要调用对于Rectangle惟一的函数。
使用dynamic_cast操作符的程序必须包含<typeinfo>头文件,且要将运行时类型信息作为编译选项。
Dynamic_cast操作符使用内部运行时类型信息数据结构来执行它的检查和转换功能。
1.1.2对引用进行向下类型转换如果使用引用而不是指针,当目标中没有指定的类时,dynamic_cast将抛出bad_cast(错误类型转换)异常。
清单1.2演示了这一行为。
////////////////////////////////////////// File Name: pr27002.cpp////////////////////////////////////////#include <typeinfo>#include <iostream>////////////////////////////////////////// Define the Control class.////////////////////////////////////////class Control{public:virtual void foo() {}};////////////////////////////////////////// Derive classes from Control.////////////////////////////////////////class TextBox : public Control { };class EditBox : public TextBox { };class Button : public Control { };////////////////////////////////////////// Paint Control objects.////////////////////////////////////////void Paint(Control& cr){try{TextBox& ctl = dynamic_cast<TextBox&>(cr);std::cout << "Paint a TextBox" << std::endl;}catch(std::bad_cast){std::cout << "nonTextBox, can't paint" << std::endl;}}////////////////////////////////////////// The main() function.////////////////////////////////////////int main(){// Instantiate and paint Control.Control ct;Paint(ct);//将运行错误// Instantiate and paint Button.Button bt;Paint(bt);//将运行错误// Instantiate and paint TextBox.TextBox tb;Paint(tb);// Instantiate and paint EditBox.EditBox eb;Paint(eb);return 0;}清单1.2说明当对象(TextBox、EditBox)派生于它所转换的类型(TextBox)时,dynamic_cast将正常工作。