前言
"打牢基础,万事不愁" .C++的基础语法的学习."学以致用,边学边用",编程是实践性很强的技术,在运用中理解,总结.
引入
RTTI是C++11加入的内容,可以应用在简单或复杂的场合下。简单理解RTTI的原理,尝试推导typeid的相关内容。以<C++ Prime Plus> 6th Edition(以下称“本书”)内容为参考。
RTTI的3个元素
dynamic_cast:按翻译的字面意思---动态转换。派生类指针转化为基类指针
typeid和type_info:指出类型及类型信息。
dynamic_cast的理解
本书内容写得比较多,如果有多态的概念,应该很好理解什么是“动态转换”(dynamic_cast)
回顾多态
基类引用可以指向派生类对象----多态的基本概念。或者说派生类对象可以转成基类引用
同理:派生类指针可以转化成基类指针。基类指针调用虚方法时,实则调用派生类方法
dynamic_cast的写法
dynamic_cast<想转化成的指针类型>(被转化的指针) ;
函数原型:dynamic_cast<Type *>(pt)
当被转化的指针pt属于Type类型,或者从Type直接或间接派生而来,则运算符的返回值是一个Type类型的指针。即
Type * tp=dynamic_cast<Type *>(pt) //tp是Type类型指针,被用来接收运算后的返回值
如果被转化指针pt不满足“属于Type类型,或者从Type直接或间接派生而来”这个条件,上面式子仍然成立,但结果为0(或nullptr)---空指针
dynamic_cast的用法
根据dynamic_cast 的定义,他的用法是比较固定的,大致如下:
//伪代码
Type* tp;
if(tp=dynamic_cast<Type*>(pt)){
tp->fun();
}
先判断能否转换,如果可以调用方法。否则转换后结果为0,也就不执行方法调用。
此处代码与平时写的代码稍微有点区别,一般if里用的是比较表达式或者逻辑表达式,用赋值表达式(=)通常情况下是误用相等判断表达式(==),但这里确实是赋值表达式,因为转换不成功,tp的值是0,由此决定分支结构的走向。
====================内容分割线=============================================
以下内容属于推测 ,不可用
dynamic_cast的函数定义
尝试写出 dynamic_cast的函数定义
//伪代码
template<class Type>
Type* dynamic_cast(Type* t){
if(t属于继承序列)
return t;
else
return 0;
}
问题是:在模板函数定义后,调用时如何使用类型指针。
这里的Type需要包含自定义类型,内置数据类型,甚至模板类型(模板类也可以继承)
如何用表达式表达出一个类属于另一个类对象的直接和间接继承关系
====================内容分割线============================================
typeid的理解
<C++ Prime Plus> 6th Edition(以下称“本书”)P646内容:
typeid运算符使得能够确定两个对象是否为同种类型。它与sizeof有些相像,可以接受两种参数:类名和结果为对象的表达式。
----解读:红色部分说明了typeid的作用。如果只是使用typeid也够了。
以下是typeid运算符的使用:
/*已测试*/
/*typeid运算符的使用*/
#include<iostream>
#include<typeinfo>
using namespace std;
class Demo{}; //声明class类
template<class T> //声明泛型类
class DemoT{};
int main(void) {
const type_info& ti = typeid(3); //1参数使用值
cout << "使用值得到的类型是:" << ti.name() << endl;
double d = 0.5;
const type_info& ti2 = typeid(d); //2参数使用变量
cout << "使用变量得到的类型是:" << ti2.name() << endl;
const type_info& ti3 = typeid(string); //3参数使用已标准化的类名
cout << "使用标准类名string得到的类型是:" << ti3.name() << endl;
const type_info& ti4 = typeid(Demo); //4参数使用自定义class类
cout << "使用自定义class类名Demo得到的类型是:" << ti4.name() << endl;
const type_info& ti5 = typeid(DemoT<int>); //5参数使用自定义模板类
cout << "使用自定义模板类名DemoT<int>得到的类型是:" << ti5.name() << endl;
const type_info& ti6 = typeid(DemoT<int>*); //6参数使用指针
cout << "使用自定义模板类名DemoT<int>的指针得到的类型是:" << ti6.name() << endl;
const type_info& ti7 = typeid(Demo&); //7参数使用引用,结果同4
cout << "使用自定义class类名Demo引用得到的类型是:" << ti7.name() << endl;
}
结果:
使用值得到的类型是:int
使用变量得到的类型是:double
使用标准类名string得到的类型是:class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >
使用自定义class类名Demo得到的类型是:class Demo
使用自定义模板类名DemoT<int>得到的类型是:class DemoT<int>
使用自定义模板类名DemoT<int>的指针得到的类型是:class DemoT<int> *
使用自定义class类名Demo引用得到的类型是:class Demo
说明:
接收typeid返回值的数据类型必须是const type_info&,使用const type_info或 type_info&都将报错,以此可以推出typeid的返回类型是const,但为何不能用变量const type_info接收原因未知.
=========================内容分割线========================================
typeid的使用也是比较容易的,尝试还原出他的函数定义,加深一点思考
首先,type_info的类定义:
思路:type_info调用name()函数得到了类名的字符串,还有重载了"=="和"!="运算符,所以尝试给出定义如下:
//尝试还原type_info定义,不一定准确
//要求string类也重载operator==()和operator!=(),字符串比较
class type_info{
string name;
public:
type_info(const string& na):name(na){} //构造函数
string name(){return name;}
bool operator==(const type_info& ti){ //重载相等比较运算符
return this.name()==ti.name();
}
bool operator!=(const type_info& ti){ //重载不等比较运算符
return this.name()!=ti.name();
}
}
尝试typeid运算符的定义
typeid可以接受类名和对象名
C++没有反射机制(至少本书没看见),那么是怎样得到类名的呢。首先想到函数重载
//伪代码
const type_info& typeid(int a){
return type_info("int");
}
const type_info& typeid(double a){
return type_info("double");
}
起码对C++底层来说,内置数据类型和标准类型如string,可以识别.对于自定义class类和模板类,在他们编译的时候,将他们的信息注册到某个位置,并且加入类似代码.当然这样做肯定比较麻烦.
假设有一个类叫TypeName,它的对象是类名,可被typeid作为形参而使用。因此存在以下函数
const type_info& typeid(TypeName tn); //函数原型
要求TypeName能识别所有类型及其对象,函数定义大概是这个样子:
//伪代码
const type_info& typeid(TypeName tn){
if(tn被识别为int类型)
return type_info("int");
else if(tn被识别为double类型)
return type_info("double");
... //其他
}
这个TypeName类起的作用和java里的Object类相类似,是所有类(包括模板和内置数据类型)的父类.估计要有修改C++编译器的能力才能做到这一点,所以到此为止了
.还有一个问题就是返回的引用被引用接收,照理说得不到正确结果,上面也提到过.C++语法应用:从return机制看返回指针,返回引用-CSDN博客
也许有人会说,回溯一个已经定义好的程序,写了半天什么也没出个结果,不是浪费时间和精力吗?
笔者的想法是:编程重在思考.回头再看:type_info和typeid到底干了什么?用一个全局函数typeid实现了信息的提取(放到了type_info类中),这不就是设计者的思路吗?就算自己写不出来,这种思路放到其他地方也是可以试一试的.
=========================内容分割线=======================================
其他
本书后面有个dynamic_cast和typeid用法比较的例子,typeid是显式指定类,用起来不如dynamic_cast,这个比较好理解,dynamic_cast支持多态,优先选用.
typeid的使用也是比较固定的一行代码:
//伪代码
if(typeid(类型对象,变量,值或者类名)==typeid(类型对象,变量,值或者类名)){ //如果两个数据属于同一类型
...... //想做的事
}
小结
dynamic_cast和typeid的简单理解和应用.他们的典型使用都是放在一个if当中做判断.