运行时类型识别(RTTI)两个重要运算符:
oop回顾:
RTTI运算符中的dynamic_cast就是针对第二点将编译时的检查放在运行时进行。运算符作用于某种类型的指针或引用,且该类型具有虚函数,运算符将使用被绑定对象的dynamic type而非static type。
RTTI运算符使用场景:拥有一个基类指针,但想使用派生类专属的操作(派生类肯定有自己独特的函数而非虚函数,依赖动态类型选择虚函数版本不适合这种情况)。
①dynamic使用形式:
/*type可以是指针、引用、右值引用 e的dynamic_type必须是type类型,但是其static type类型可以是 type的公有派生类、公有基类或type类型。 */ type Target = dynamic_cast<type>(e);
②条件中使用dynamic_cast,既可以进行类型转换,又能进行条件检查:
if(Derived* kid = dynamic_cast<Derived*>(father)){ //直接使用转换后的kid指针 kid->DoSomething(); }else{ //转换不通过,会返回0 cout<< "cast failed" <<endl; } //引用类型转换失败不会返回0,而是抛出std::bad_cast
使用测试:
class Actor{ public: Actor(int Input):Num(Input){} virtual void print()const{cout<< "Actor"<<endl;} void Funtion(){cout<<Num<<endl;} public: int Num; }; class ActorChild: public Actor{ public: ActorChild(int InputFirst,int InputSecond):Actor(InputFirst),ChildNum(InputSecond){} virtual void Print()const override{cout<< "ActorChild"<<endl;} void ChildFuntion(){cout<<ChildNum<<endl;} public: int ChildNum; }; class Testt:public ActorChild{ public: Testt(int InputFirst,int InputSecond):ActorChild(InputFirst,InputSecond){} }; int main() { //对②的测试,条件中进行类型转换 ActorChild A(2,1); Actor* OtherActor = &A; if(ActorChild* Controller = dynamic_cast<ActorChild*>(OtherActor)){ //成功转换,并使用了派生类的虚函数 Controller->Print(); //成功转换,并使用了派生类的专有函数 Controller->ChildFuntion(); } //Controller->print(); 错误,Controller只在上面的条件块中作用。 //对①的测试,e的dynamic type类型不是type类型 Actor* B = new Actor(2); ActorChild* C = new ActorChild(2,2); //对①的测试,e的dynamic type类型不是type类型 Testt* D = new Testt(2,2); Actor* E = D; ActorChild* F = D; if(Testt* ptr = dynamic_cast<Testt*>(B)){} else{cout << "castB failed"<<endl;} if(Testt* ptr = dynamic_cast<Testt*>(C)){} else{cout << "castC failed"<<endl;} if(Testt* ptr = dynamic_cast<Testt*>(D)){} else{cout << "castD failed"<<endl;} if(Testt* ptr = dynamic_cast<Testt*>(E)){} else{cout << "castE failed"<<endl;} if(Testt* ptr = dynamic_cast<Testt*>(F)){} else{cout << "castF failed"<<endl;} } /* output: ActorChild 1 castB failed castC failed */
基本使用:
int main() { ActorChild* A = new ActorChild(1,1); Actor* B = A; //typeid可以作用于指针,但是typeid操作结果返回的是static type。 if(typeid(A)==typeid(B)){ cout<<"1同一类型"<<endl; } if(typeid(*A)==typeid(*B)){ cout<<"2同一类型"<<endl; } //typeid可以作用于指针,但是typeid操作结果返回的是static type。 if(typeid(A)==typeid(ActorChild)){ cout<<"3同一类型"<<endl; } if(typeid(A)==typeid(ActorChild*)){ cout<<"4同一类型"<<endl; } if(typeid(*A)==typeid(ActorChild)){ cout<<"5同一类型"<<endl; } } /*output: 2同一类型 4同一类型 5同一类型 */
结论-
使用场景:为具有继承关系的类实现相等运算符,且是深度比较(类型和成员数据)。在没有RTTI参与下,为什么不使用operator==和equal()虚函数来直接实现相等判断?
class Actor{ public: Actor(int Input):Num(Input){} bool operator==(const Actor& Other){ return Num==Other.Num; } private: int Num; }; class ActorChild: public Actor{ public: ActorChild(int InputFirst,int InputSecond):Actor(InputFirst),ChildNum(InputSecond){} bool operator==(const ActorChild& Other){return ChildNum==Other.ChildNum; } private: int ChildNum; };
仅有operator下,派生类无法直接访问基类私有成员进行比较,故需要借助equal虚函数的帮助。但虚函数形参无法修改,形参为基类只能比较基类部分而派生类部分无法比较,所以运行时调用该函数进行比较行不通。而使用RTTI在operator中先进行类型判断(typeid),然后equal()中再通过dynamic_cast将形参捕获的基类引用进行类型转换,从而实现对派生部分的比较。
class Actor{ public: Actor(int Input):Num(Input){} bool operator==(const Actor& Other){ cout<<"operator=="<<endl; //this->equal()为虚调用 return typeid(*this)==typeid(Other) && this->equal(Other); } virtual bool equal(const Actor& rhs)const{ cout<<"Actor equal"<<endl; return Num==rhs.Num; } protected: int Num; }; class ActorChild: public Actor{ public: ActorChild(int InputFirst,int InputSecond):Actor(InputFirst),ChildNum(InputSecond){} virtual bool equal(const Actor& rhs)const override{ cout<<"ActorChild equal"<<endl; auto &temp = dynamic_cast<const ActorChild&>(rhs); return ChildNum == temp.ChildNum && Actor::equal(rhs) ; }; protected: int ChildNum; }; int main() { ActorChild* a = new ActorChild(2,2); ActorChild* b = new ActorChild(2,2); ActorChild* c = new ActorChild(1,2); Actor* d = b; Actor* e = c; if(*a==*d){ cout << "一样"<<endl; }else{ cout << "不一样"<<endl; } if(*a==*e){ cout << "一样"<<endl; }else{ cout << "不一样"<<endl; } } /* operator== ActorChild equal Actor equal 一样 operator== ActorChild equal Actor equal 不一样*/
在派生类中比较派生部分,如果想要进一部比较基类部分,则可以使用回避虚函数的机制,通过作用域运算符执行基类的特定版本,该操作适用于派生类虚函数调用其覆盖的基类虚函数版本。