RTTI
dynamic_cast 和type_id特别适用于 对基类指针引用执行某个派生类操作而该派生类操作不是虚函数(非常拧巴)
与虚函数成员相比RTTI有潜在风险
基本例子, 基类至少含有要一个虚函数否则报错cannot dynamic_cast ‘ptr’ (of type ‘class Father*’) to type ‘class son*’ (source type is not polymorphic)
class Father{
//基类至少有一个虚函数
virtual void func(void){}
};
class son:public Father{
};
int main(){
Father * ptr = new son();
if(son* dp = dynamic_cast<son *>(ptr))
{
cout << "RTTI\n";
}
else{
cout<<"failed\n";
}
return 0;
}
RTTI 引用例子
void f(const Father & rf){
try{
const son & rs = dynamic_cast<const son &>(rf);
cout <<"RTTI ok\n";
}
catch(...){
cout <<"nothing";
}
return;
}
typeid比较动态对象
下面很重要
- typeid作用域指针时 需要确保对象存在,否则会抛错"bad_typeid",用之前最好判断一下指针是否非null
- typeid和dyanmaic_cast一样,需要至少一个虚函数存在于基类
- typeid如果作用域指针(而非指针对象)返回结果是该指针静态类型
typeid比较(指针和引用的)动态对象
//typeid用于比较指针的动态对象
Father *pf = new son();
son * ps = new son();
cout << boolalpha << (typeid(*pf)==typeid(*ps)) << endl;//true 两者指向同一动态对象
//typeid用于比较引用的动态对象
Father & rf = (*pf);
son & rs = (*ps);
cout << boolalpha << (typeid(rf)==typeid(rs)) << endl;//true
注意如果上述son *ps = nullptr;
会抛错std::bad_typeid
这种比较需要确保对象存在。
如果只想确定类型可以用typeid和类型比较
typeid比较对象和类型
也要确保对象存在,否则会跑错
Father *pf = new son;
if (typeid(*pf)==typeid(son))
{
cout << "son\n";
/* code */
}
else if(typeid(*pf)==typeid(Father))
{
cout << "Father\n";
}
typeid如果作用域指针(而非指针对象)返回结果是该指针静态类型
//typeid用于比较指针本身而非对象
Father *pf = new son;
if (typeid(pf)==typeid(son*))
{
cout << "son\n";
/* code */
}
else if(typeid(pf)==typeid(Father*))
{
cout << "Father pointer \n";
}
typeid和std::type_info
- typeid是个operatorhttps://en.cppreference.com/w/cpp/language/typeid
- std::type_info是个STL库类 存在于头文件下 https://www.cplusplus.com/reference/typeinfo/type_info/
type_info用来打类型
int i;
int * pi;
std::cout << "int is: " << typeid(int).name() << '\n';
std::cout << " i is: " << typeid(i).name() << '\n';
std::cout << " pi is: " << typeid(pi).name() << '\n';
std::cout << "*pi is: " << typeid(*pi).name() << '\n';
输出
int is: i
i is: i
pi is: Pi
*pi is: i
使用RTTI
- Primer19.2.3 相等运算符例子
相等运算符
目的是定义一套相等运算符,Base对比Base,Derived对比Derived
如果相等运算符读入Base指针,只能比较Base对象
equal包了类对象的所有成员的比较
class Base{
//友元相等运算符 友元是基类自己的友元
friend bool operator==const Base &,const Base&);
protected:
virtual bool equal(const Base &) const;//比较
};
class Derived:public Base{
protected:
//virtual函数必须保持形参一致,即使派生类也只能用基类引用类型做入参
virtual bool equal(const Base &) const;//比较
};
bool operator==(const Base & lhs,const Base &rhs){
//rtti确保动态类型一致
//调用的equal和lhs的动态类型一致
return typeid(lhs)==typeid(rhs)&&lhs.equal(rhs);
}
//别忘了 virtual和不出现在函数外
bool Derived::equal(const Base &rhs){
//必须类型转换的原因 只有使用派生类引用才能对比派生类独有对象
auto r = dynamic_cast<const Derived &>(rhs);
}
把几个例子填满
class Base{
//友元相等运算符 友元是基类自己的友元
friend bool operator==(const Base &,const Base&);
protected:
virtual bool equal(const Base &) const;//比较
public:
string name;
Base(const string &n):name(n){}
};
class Derived:public Base{
protected:
//virtual函数必须保持形参一致,即使派生类也只能用基类引用类型做入参
virtual bool equal(const Base &) const;//比较
public:
double thre;
Derived(const string &n,const double & t):Base(n),thre(t){}
};
//别忘了 virtual和不出现在函数外
bool Derived::equal(const Base &rhs) const {
//必须类型转换的原因 只有使用派生类引用才能对比派生类独有对象
auto r = dynamic_cast<const Derived &>(rhs);
return (this->name==rhs.name)&&(this->thre==r.thre);
}
bool Base::equal(const Base &rhs) const {
return this->name==rhs.name;
}
bool operator==(const Base & lhs,const Base &rhs){
//rtti确保动态类型一致
//调用的equal和lhs的动态类型一致
return typeid(lhs)==typeid(rhs)&&lhs.equal(rhs);}
int main(){
Base ba("Alice"),bb("Jack");
Derived da("Migi",0.4),db("Jack",0.4),dc("Migi",0.4);
cout<<boolalpha <<(da==dc)<<endl;
cout<<boolalpha <<(bb==db)<<endl;
cout<<boolalpha <<(ba==bb)<<endl;
return 0;
}
参考
Primer