C/C++教程

C++RTTI运算符

本文主要是介绍C++RTTI运算符,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

概述

运行时类型识别(RTTI)两个重要运算符:

  • typeid:返回表达式类型。
  • dynamic_cast:将基类指针或引用安全转换成派生类的指针或引用。

oop回顾:

  1. 不存在从基类向派生类的隐式转换。原因是因为派生类对象中包含基类部分和派生类部分,而基类对象中只包含有基类部分。
  2. 不存在基类指针或引用绑定在一个派生类对象,又将基类转换为派生类。该操作在编译时就会引发错误,原因是编译器无法确定某个特定转换是运行时安全的,其只能检查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运算符的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!