C++RTTI运算符
2022/3/21 20:30:59
本文主要是介绍C++RTTI运算符,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
概述
运行时类型识别(RTTI)两个重要运算符:
- typeid:返回表达式类型。
- dynamic_cast:将基类指针或引用安全转换成派生类的指针或引用。
oop回顾:
- 不存在从基类向派生类的隐式转换。原因是因为派生类对象中包含基类部分和派生类部分,而基类对象中只包含有基类部分。
- 不存在基类指针或引用绑定在一个派生类对象,又将基类转换为派生类。该操作在编译时就会引发错误,原因是编译器无法确定某个特定转换是运行时安全的,其只能检查static type。
dynamic_cast运算符
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 */
typeid运算符
基本使用:
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同一类型 */
结论-
- typeid运算对象可以是表达式或类型名。
- typeid运算结果返回一个来自typeinfo中的type_info类常量对象。
- typeid可以作用于指针(更宽泛地说,运算对象不包括包含虚函数的类),typeid操作结果返回的是static type。
- typeid也可以作用于对象,若该对象是包含虚函数的类,则会运算对象的动态类型。
使用场景:为具有继承关系的类实现相等运算符,且是深度比较(类型和成员数据)。在没有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 不一样*/
在派生类中比较派生部分,如果想要进一部比较基类部分,则可以使用回避虚函数的机制,通过作用域运算符执行基类的特定版本,该操作适用于派生类虚函数调用其覆盖的基类虚函数版本。
这篇关于C++RTTI运算符的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-05-15PingCAP 黄东旭参与 CCF 秀湖会议,共探开源教育未来
- 2024-05-13PingCAP 戴涛:构建面向未来的金融核心系统
- 2024-05-09flutter3.x_macos桌面os实战
- 2024-05-09Rust中的并发性:Sync 和 Send Traits
- 2024-05-08使用Ollama和OpenWebUI在CPU上玩转Meta Llama3-8B
- 2024-05-08完工标准(DoD)与验收条件(AC)究竟有什么不同?
- 2024-05-084万 star 的 NocoDB 在 sealos 上一键起,轻松把数据库编程智能表格
- 2024-05-08Mac 版Stable Diffusion WebUI的安装
- 2024-05-08解锁CodeGeeX智能问答中3项独有的隐藏技能
- 2024-05-08RAG算法优化+新增代码仓库支持,CodeGeeX的@repo功能效果提升