RTTI,run-time type identification。
动态类型:也叫运行时类型,指的是变量直到运行时,才能确定它的类型。
静态类型:指的是变量在编译时就能确定的类型。
由两个运算符实现:
- typeid:返回表达式的类型
- dynamic_cast:将基类指针、引用转换成派生类的指针、引用,我们常说的动态类型转换。 ```cpp
Derived d; Base p = &d; // p的静态类型是Base,动态类型是Derived*。
void fuck(Base p){
// p运行时指向的是派生类对象,我们想调用派生类特有的成员,这时候就需要将
// 基类的指针引用转换成派生类的指针引用进行操作了。
if(Derived pShit = dynamic_cast
<a name="pZgR1"></a>
# dynamic_cast
将基类的**指针**、**引用**转换成派生类的指针、引用。
```cpp
/*****************dynamic_cast使用形式***********************/
// type必须是类类型。
dynamic_cast<type*> (e); // 指针形式,e必须是指针,否则转换必定失败。
dynamic_cast<type&> (e); // 左值引用,e必须是左值引用,否则转换必定失败。
dynamic_cast<type&&> (e); // 右值引用,e不能是左值引用,否则转换必定失败。
// e的类型必须满足以下条件之一,才可能转换成功
// 1、e的类型是type类型的子类。
// 2、e的类型是type类型的共有基类。
// 3、e的类型就是type的类型。
// 转换失败时,会发生2中行为:
// 1、如果是转换指针形式,则返回0,也就是空指针。
// 2、如果是转换引用形式,则抛出异常std::bad_cast,
// 我们应该通过try catch捕获异常来判断是否成功
/*****************dynamic_cast使用形式***********************/
// 指针形式的使用例子
if(Derived *pShit = dynamic_cast<Derived*>(p)){
pShit->DerivedMethod(); //调用派生类的方法。
}
else{} //转换失败,返回空指针
// 不建议这么使用指针形式,
Derived *pShit = dynamic_cast<Derived*>(p);
if(pShit){}
// 引用形式的例子
try {
const Derived &d = dynamic_cast<const Derived&>(b);
// 使用b引用的 Derived 对象
}
catch (bad_cast) {} // 处理类型转换失败的情况
typeid
返回表达式的类型信息,可能是动态类型,可能是静态类型。
/*****************typeid使用形式***********************/
// typeid运算符的使用形式
// e:任意表达式、或者是类型的名字
// 返回type_info或者它的派生类对象的常量引用
// type_info是类类型,定义在头文件typeinfo中,在下面有介绍
const type_info& info = typeid(expr); // 任意表达式expr
const type_info& info = typeid(T); // 类型名字T
// e是引用,返回所引用对象的类型
// e是数组、函数,返回数组、函数类型,不是自动转换成指针类型。
// e不是类类型,返回静态类型
// e的类类型不包含任何虚函数,返回静态类型
// e是左值,且是有虚函数的类类型时,返回动态类型。
// 当时返回动态类型时,会对表达式求值
// 当时返回静态类型时,不对表达式求值
/*****************typeid使用例子***********************/
Derived *dp = new Derived;
Base *bp = dp;
// 在运行时比较两个对象的类型
// 两个指针都指向Derived对象
if(typeid(*bp) == typeid (*dp)) {
// bp和dp指向同一类型的对象
}
// 检查运行时类型是否是某种指定的类型
if(typeid(*bp) == typeid(Derived)){
// bp实际指向Derived对象
}
//注意!!!这种检查,永远是失败的,前者返回的永远是指针类型Base*
if (typeid(bp) == typeid(Derived)) {
// 此处的代码永远不会执行
}
type_info
C++标准规定了type_info必须包含的操作,而不同编译器可能会添加额外的操作,
t1 == t2; // 如果type_info对象t1和t2表示同一种类型,返回true;否则返回false
t1 != t2; // 如果type_info对象t1和t2表示不同的类型,返回true;否则返回false
t.name(); // 返回一个C风格字符串,表示类型名字的可打印形式。编译器相关。
t1.before(t2); // 返回一个bool值,表示t1是否位于t2之前。
// before所采用的顺序关系是依赖于编译器的
// type_info没有默认构造函数,拷贝控制成员都是delete的。
// 只能通过typeid返回typeinfo
例子
在派生体系中实现一个equal函数用于判断类对象是否相等(类型相同且所有成员数值相同)。
class Base{
friend bool operator==(const Base &,const Base &);
public:
// Base的接口成员
protected:
virtual bool equal(const Base &) const;
// Base的数据成员和其他用于实现的成员
}
class Derived: public Base{
public:
// Derived的其他接口成员
protected:
bool equal(const Base &r) const{
// 我们清楚这两个类型是相等的,所以转换过程不会抛出异常
auto r = dynamic_cast<const Derived&>(rhs);
// 执行比较两个Derived对象的操作并返回结果
......
}
// Derived的数据成员和其他用于实现的成员
}
bool operator==(const Base &lhs , const Base &rhs){
// 如果typeid不相同,返回false; 否则虚调用equal
return typeid(lhs) == typeid(rhs) && lhs.equal(rhs);
}