运行期形式信息
RTTI
面向对象架构中我们频繁的需要将一个对象或者一个对象的引用或者一个对象的指针,沿着类库的继承层次进行转型,这个转型必须能在程序运行期间确保他的有效性,而这个有效性的保证没有运行期有效信息是不可能完成的。运行期形式信息就意味着在运行期间这些对象所从属的形式的信息。
在C++标准库中提供了一个type_info这样一个形式信息类,用来记录这些类的形式信息,但是你不能直接使用type_info类,你只能使用typeid操作符来获取一个特定的表达式的形式信息。
C++提供了四个重要的转型操作符:
-dynamic_cast:动态转型
-static_cast:静态转型
-reinterpret_cast:复诠转型
-const_cast:常量转型
type_info类:编译器实现的动态信息都在这个这个类中,他保存的是用于在程序运行时保存数据对象的形式信息,也就是说他是一个动态形式信息类,程序运行期间对象的形式信息都要通过type_info来保存,而且只能使用typeid操作符来访问他。有一个成员函数名字叫name()可用用它来获得类的名称。
typeid操作符:
例子:
#include<typeinfo> Programmer p; Employee &e = p; cout <<typeid(e).name()<<endl:
我们实际上将获取的是"Programmer";这里有一个前提假设,Programmer是Employee的一个派生类。
dynamic_cast
动态转型的三种方式
-向上转型:沿着类继承的层次向基类转型
-向下转型:沿着类的继承层次向派生类转型
-交叉转型:沿着类的多重继承层次横向转型
当一个指针的动态转型
-正确执行时,结果为指向目标类对象的指针
-错误执行时,结果为0/NULL(C++11:nullptr)
引用的动态转型
-正确执行时,结果为目标对象类的引用
-错误执行时,引发bab_cast异常
例子
//假设软件公司包括程序员和经理两类职员,需要按照不同的规则支付薪水和奖金,如何实现 class Employee { public: virtual void PaySalary(); virtual void PayBonus(); }; class Manager:public Employee { public: virtual void PaySalary(); virtual void PayBonus(); }; class Programmer:public Employee { public: virtual void PaySalary(); virtual void PayBonus(); }; class Company { public: virtual void PayRoll(Employee *e); virtual void PayRoll(Employee &e); private: vector<Employee*>_employees;//向量数据结构 }; void Company::PayRoll(Employee *e) { Programmer *p = dynamic_cast<Programmer*>(e);//dynamic_cast是个模板,所以带一对尖括号,尖括号里面写着目标型 //动态转型将Employee类型的指针转换成Programmer类型的指针 if(p) //因为指针的动态转型成功的话返回目标类对象指针,否则返回0/nullptr { p->PaySalary(); p->PayBonus(); } else e->PaySalary(); };
如果是一个引用的转型:
void Company::PayRoll(Employee &e) { try { Programmer &p = dynamic_cast<Programmer&>e; p.PaySalary(); p.PayBonus(); } catch(std::bad_cast) { e.PaySalary(); } };
static_cast:
-静态转型不仅可以用于指针与引用,还可以用于其他类型转换。
-一般用于非类型式的普通数据的对象转型
注意:静态转型不进行运行期形式检查,不安全。如果转型失败,结果是未知的
也可以在类继承层次上进行向上或者向下的转型,也可以交叉转型但是出错率非常高,一般不用。
const_cast:
用于取消或者设置量的const状态,比如有的量是const类型,我想修改一下他的值,那么就需要进行一次转型。需要注意的是,如果原始数据对象不能写入,则取消常量修饰可能会导致未知结果,const_cast的最常用的场景是,再进行参数传递的时候实际参数与形式参数的const一次状态变化
例子:
#include<iostream> class ConstCastTest { public: void SetNum(int num){_num = num;} void PrintNum() const; private: int _num; }; void ConstCastTest::PrintNum()const //如果想在PrintNum里面修改_num那个成员的值,那么就必须临时的将this指针转型 { //临时取消常量约束,修改目标对象的内容 const_cast<ConstCastTest*>(this)->num--; std::cout<<_num; }
这里需要对this转型的原因就是这是一个const函数,所以传过来的那个this指针,他不是指向ConstCastTest而是指向const ConstCastTest,指向常对象的指针。
复诠转型
将任意形式的数据对象转型为目标形式,即重新解释其位序列的意义
可以进行整形与指针的互转
需要程序员来确保转换的正确
在64位操作系统下指针可能是64位宽的而整形可能是32位,复诠转型可能造成丢失数据
#include<iostream> using namespace std; int f(void* p) { unsigned long n = reinterpret_cast<unsigned long>(p);//我的机器上如果使用int会报错精度丢失,所以在这里换成了long return n; } int main() { int a[8] = {1,2,3,4,5,6,7,8}; int n = f(a); cout<<n<<endl; }

浙公网安备 33010602011771号