运行时类型识别(run-time type identification,RTTI)的功能由俩个运算符实现:
1.typeid运算符,用于返回表达式的类型
2.dynamic_cast运算符,用于将父类的指针或引用安全地转换成子类的指针或引用
当我们将这俩个运算符用于某种类型的指针或引用,并且该类型含有虚函数时,运算符将使用指针或引用所绑定对象的动态类型
适用情况:我们想使用父类对象的指针或引用执行某个子类操作并且该操作不是从父类继承并重新覆写的虚函数,而是子类自己定义的其他成员(一般来说只要有可能我们应该尽量使用虚函数,当操作被定义成虚函数时,编译器将根据对象的动态类型自动地选择正确的函数函数版本),则需要使用dynamic_cast进行转换。
dynamic_cast运算符的使用形式:
dynamic_cast<type*>(e) //e必须是一个有效的指针
dynamic_cast<type&>(e) //e必须是一个左值
dynamic_cast<type&&>(e) //e不能是左值
type应该满足的条件:
1.type必须是一个类类型
2.该类型含有虚函数
e类型应该满足的条件(满足任意一个即可):
1.e的类型是目标type的公有派生类
2.e的类型是目标type的公有基类
3.e的类型就是目标type的类型
转换目标为指针:
成功:返回转换目标类型的指针
失败:返回NULL
class A
{
public:
A() :_a(10) {}
virtual void func()
{
cout << "A::func()" << endl;
}
//private: //如果设置为private后则它不会被子类B继承,所以在下面赋值兼容的时候不能使用这个成员
int _a;
};
class B :public A
{
public:
B() :_a(100), _b(88) {}
virtual void func()
{
cout << "B::func()" << endl;
}
void foo()
{
cout << "B::foo()" << endl;
}
int _a;
int _b;
};
//因为C++中类型很重要,如果写成A* a = new A(); 则a是A的指针,编译器按A的结构来认识他,不可能知道B中的东西,
//如果写成A* a = new B(); 编译器按B的结构来认识它,dynamic_cast只是确认下a指示的是不是B的结构而已
int main()
{
//注意这里不能写成A* a=new A();
//A* a = new B(); //这么写也可以
B b;
A* a = &b; //赋值兼容 子类对象的地址赋值给父类指针
cout << "a->_a= " << a->_a << endl; //10
a->func(); //B::func() 由于b中virtual函数的覆写已经覆盖了虚函数表中A::func(),所以会调用B::func()
if (B* b = dynamic_cast<B*>(a))
{
cout << "change success!!!" << endl;
b->func();
//成功转换后则可以调用子类中自己特有的函数了
b->foo();
cout << "b->_a= " << b->_a << endl; //100 虽然父子类中有重名的_a,但是俩者是俩个独立的存在
cout << "b->_b= " << b->_b << endl; //88 _b为b中独有的成员
}
else
{
cout << "change fail!!!" << endl;
//转换失败则不能使用指针b,只能使用指针a的操作
a->func();
cout << "a->_a= " << a->_a << endl;
}
}
因为C++中类型很重要,如果写成A* a = new A(); 则a是A的指针,编译器按A的结构来认识他,不可能知道B中的东西,如果写成A* a = new B(); 编译器按B的结构来认识它,dynamic_cast只是确认下a指示的是不是B的结构而已。
值得注意的一点是,我们在条件部分定义的b,这样做的好处是可以在一个操作中同时完成类型转换和条件检查俩项任务,而且,指针b在if语句外部是不可访问的,一旦转换失败,即使后序的代码忘了做相应的判断,也不会接触到这个未绑定的指针,从而确保程序是安全的。
转换目标为引用:
成功:返回转换目标类型的引用
失败:程序抛出一个名为std::bad_cast的异常,该异常在typeinfo标准库头文件中,所以使用try和catch来处理转换结果
class A
{
public:
A():_a(10) {}
virtual void func()
{
cout << "A::func()" << endl;
}
//private: //如果设置为private后则它不会被子类B继承,所以在下面赋值兼容的时候不能使用这个成员
int _a;
};
class B :public A
{
public:
B() :_a(100),_b(88) {}
virtual void func()
{
cout << "B::func()" << endl;
}
void foo()
{
cout << "B::foo()" << endl;
}
int _a;
int _b;
};
int main()
{
A a1;
B b;
//A& a=a1; //会导致转换失败,走catch逻辑
A& a = b; //赋值兼容
cout << "a->_a= " << a._a << endl; //10
a.func(); //B::func() 由于b中virtual函数的覆写已经覆盖了虚函数表中A::func(),所以会调用B::func()
try
{
B&b = dynamic_cast<B&>(a);
//成功则使用b
cout << "change success!" << endl;
b.func();
b.foo();
cout << "b._a= " << b._a << endl; //100
cout << "b._b= " << b._b << endl; //88
}
catch (bad_cast)
{
//失败则处理类型转换失败的情况
cout << "change fail!" << endl;
}
}