typeid运算符:用于返回表达式的类型
dynamic_cast运算符:用于将基类的指针或引用安全地转换成派生类的指针或引用
将这两个运算符用于某种类型的指针或引用,当该类型含有虚函数,运算符将使用指针或引用所绑定对象的动态类型
适用的例子:使用基类对象的指针或引用执行某个派生类操作并且该操作不是虚函数
假设无法使用虚函数:使用RTTL运算符
注意:存在风险,建议定义虚函数而非直接接管类型管理的重任
使用形式
//type必须是一个类类型,通常情况应该含有虚函数 dynamic_cast(e)//e必须是一个有效的指针 转换目标是指针类型并且失败了,则结果为0 dynamic_cast (e)//e必须是一个左值 失败抛出一个bad_cast异常 dynamic_cast (e)//e不能是左值 //e的类型必须符合以下三个条件中的任何一个: //e的类型目标type的公有派生类,e的类型是目标type的公有基类或者e的类型就是目标type的类型 //符合转换成功,否则失败
指针类型的dynamic_cast
struct Base
{
virtual ~Base(){};
};
struct Derived :public Base
{
};
int main()
{
Base* bp;
bp = new Derived;
//在条件部分执行dynamic_cast操作可以确保类型转换和结果检查在同一条表达式中完成
if (Derived* dp = dynamic_cast(bp))
{
puts("use the Dereived object to which dp points");
}
else //bp指向的是Base对象 则指向下面这条语句
{
puts("use the Base object to which bp points ");
}
}
引用类型的dynamic_cast
void f(const Base&b)
{
try
{
const Derived &d =dynamic_cast(b);
}catch(bad_cast) //bad_cast在标准库typeinfo头文件中
{
//处理类型转换失败的情况
cout<<"called with an object that is not a Derived"<
Base *bp;
//bp =new Derived; //这条就不会报错
bp =new Base;
f(*bp); //报错
}
19.2.1节练习
```cpp
class A { public:virtual ~A() {} };
class B:public A{ public:virtual ~B() {} };
class C:public B{ public:virtual ~C() {} };
class D:public B,public A{ public:virtual ~D() {} };
int main()
{
B* pa= new B;
if (C* pb = dynamic_cast(pa))
{
cout << "转换成功" << endl;
}
else
{
cout << "转换失败" << endl;
}
}
只有(b)会失败B*pa =new B;C*pb =dynamic_cast(pa); pb不能指向基类的对象,pa转换成了C指针,C指针不能new一个B对象 练习19.5 ```cpp A* pa = new C; try { const C& cc = dynamic_cast (*pa); } catch (bad_cast) { cout << "dynamic_cast failed" << endl; }
练习19.5
适用的例子:使用基类对象的指针或引用执行某个派生类操作并且该操作不是虚函数
19.2.2 typeid 运算符当typeid作用于指针时(而非对象)返回的结果是该指针的静态编译时类型
Derived*dp =new Derived; Base* bp =dp; //两个指针都指向Derived对象 //在运行时比较两个对象的类型 if(typeid(*bp)==typeid(*dp)) cout<<"指向的是同一种类型"<}
19.2.2节练习
练习19.6
class Query_base
{public:
Query_base() {};
virtual ~Query_base(){}
};
class BinaryQuery :public Query_base
{
public:
BinaryQuery() {};
virtual ~BinaryQuery() {}
};
class AndQuery :public BinaryQuery
{
public:
AndQuery() {};
virtual ~AndQuery() {}
};
int main()
{
Query_base* qp = new Query_base;
if (AndQuery* bp = dynamic_cast(qp))
{
cout << "转换成功" << endl;
}
else
{
cout << "转换失败" << endl;
}
try
{
AndQuery* pb = dynamic_cast(qp);
cout << typeid(pb).name() << endl;
if (typeid(pb) == typeid(qp))
{
cout << "convert correct" << endl;
}
}
catch (bad_cast& e)
{
cout << e.what() << endl;
}
练习19.7
Query_base* qp = new Query_base;
try
{
AndQuery&pb = dynamic_cast(*qp);
cout << typeid(pb).name() << endl;
if (typeid(pb) == typeid(qp))
{
cout << "convert correct" << endl;
}
}
catch (bad_cast)
{
cout << "dynamic_cast failed" << endl;
}
19.8
Query_base* qp = new BinaryQuery();
Query_base* pp = new AndQuery();
if (typeid(qp) == typeid(pp))
{
cout << "指针指向的类型一致" << endl;
}
else
{
cout << "类型不一致" << endl;
}
if (typeid(*qp) == typeid(AndQuery))
{
cout << "指针指向的类型一致" << endl;
}
else
{
cout << "指向的类型不一致" << endl;
}
if (typeid(*pp) == typeid(AndQuery))
{
cout << "指针指向的类型一致" << endl;
}
else
{
cout << "指向的类型不一致" << endl;
}
19.2.3 使用 RTTL
一个例子:我们想为具有继承关系的类实现相等运算符,
难点:虚函数的基类版本和派生类版本必须具有相同的形参类型,如果定义一个虚函数equal,则该函数的
形参必须是基类的引用,则该函数的形参必须是基类的引用。此时,equal函数将只能使用基类的成员,
而不能比较派生类独有的成员
类的层次关系
class Base
{
friend bool operator==(const Base&,const Base&);
public:
//Base的接口成员
protected:
virtual bool equal(const Base&)const;
//Base的数据成员和其他用于实现的成员
};
class Derived:public Base
{
public:
//Derived的其他接口成员
protected:
bool equal(const Base&)const;
//Derived 的数据成员和其他用于实现的成员
}
下面是用RTTL解决虚函数问题
类型敏感的相等运算符
bool operator==(const Base&lhs,const Base&rhs)
{
//如果typeid 不相同,返回false;否则虚调用equal
//运算对象是Base时,调用Base::equal
//运算对象是Derived时,调用Derived::equal
return typeid(lhs)==typeid(rhs)&&lhs.equal(rhs);
}
虚equal函数
bool Derived::euqal(const Base &rhs)const
{
//我们清除这两个类型是相等的,所以转换过程不会抛出异常
auto r =dynamic_cast(rhs);
//执行比较两个Derived对象的操作并返回结果
}
基类equal函数
//*this和形参都是Base对象,当前对象可用的操作对于形参类型同样有效
bool Base::equal(const Base &rhs)const
{
//执行比较Base对象的操作
}
19.2.4 type_info 类
创建对象的唯一途径是使用typeid运算符
int arr[10]; Derived d; Base*p =&d; cout<练习题 19.2.4练习https://zhuanlan.zhihu.com/p/363011304
练习19.10
class A*
class A*
class Bclass A { public: A()=default; virtual ~A() {}; }; class B:public A { public: B() = default; virtual ~B() {}; }; class C:public B { public: C() = default; virtual ~C() {}; }; int main() { A* pa = new C; cout << typeid(pa).name() << endl; C cobj; A& ra = cobj; cout << typeid(&ra).name() << endl; B* px = new B; A& Ra = *px; cout << typeid(Ra).name() << endl; }



