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
不一样*/
在派生类中比较派生部分,如果想要进一部比较基类部分,则可以使用回避虚函数的机制,通过作用域运算符执行基类的特定版本,该操作适用于派生类虚函数调用其覆盖的基类虚函数版本。
标签:typeid,ActorChild,派生类,Actor,运算符,C++,基类,RTTI,type 来源: https://blog.csdn.net/MAXXD/article/details/123641725