在 c++ 面向对象应用中,咱们经常会定义一个基类类型的指针,在运行过程中,这个指针可能指向一个基类类型的对象,也可能指向的是其子类类型的对象,那当初问题来了,咱们如何去判断这个指针到底执行了一个什么类型的对象呢?
明天咱们就聊一下这个问题,首先咱们要辨别是否容许 RTTI
,据此有不同方法。
1 容许应用 RTTI
在关上 rtti 的场景下,能够应用 dynamic_cast
和 typeid
这两个运算符来判断对象的实在类型。
1.1 应用 dynamic_cast
dynamic_cast
用于在运行时进行多态类型检查和转换,它能够将指向基类的指针转换为指向派生类的指针或援用。如果转换胜利,则阐明对象属于指标类或其派生类。如果转换失败,则返回空指针。
咱们看如下例子,咱们想判断指针 basePtr
是否指向了 Child2
类型的对象。总共进行了两次测试,第一次让该指针指向了 Child1
类型的对象,第二次则是指向了 Child2
类型的对象。
#include <iostream>class Basic {public: virtual void say() { std::cout << "我是基类" << std::endl; }};class Child1 : public Basic {public: void say() { std::cout << "我是 child 1" << std::endl; }};class Child2 : public Basic {public: void say() { std::cout << "我是 child 2" << std::endl; }};int main(){ Basic* basePtr; basePtr = new Child1(); if (dynamic_cast<Child2*>(basePtr)) { std::cout << "[test 1]指针指向了 Child2 类型对象" << std::endl; } else { std::cout << "[test 1]指针没有指向 Child2 类型对象" << std::endl; } delete basePtr; basePtr = new Child2(); if (dynamic_cast<Child2*>(basePtr)) { std::cout << "[test 2]指针指向了 Child2 类型对象" << std::endl; } else { std::cout << "[test 2]指针没有指向 Child2 类型对象" << std::endl; } delete basePtr;}
让咱们一起看看两次的打印,这是合乎咱们的预期的,应用 dynamic_cast 能够判断一个基类类型的指针是否指向了某个具体类类型。
在这里,有的敌人会好奇,我为什么增加了 say()
这么一个办法,凑数吗?的确是,就是凑数的dynamic_cast
是用于多态运行时的类型查看,如果我不减少这么一个办法,并且在基类中增加上 virtual
关键字,那就不存在多态,也就无从谈起运行时多态类型查看。上面是我将 virtual
去掉,或者罗唆删除 say()
办法的编译后果。
1.2 应用 typeid
typeid
运算符返回一个 type_info
对象,该对象蕴含类型的相干信息。通过比拟两个指针的类型信息,能够确定它们是否具备雷同的类型。这里咱们不必管 type_info
是什么货色,咱们次要看看怎么用,上面持续看看刚刚的例子。
#include <iostream>class Basic {public: virtual void say() { std::cout << "我是基类" << std::endl; }};class Child1 : public Basic {public: void say() { std::cout << "我是 child 1" << std::endl; }};class Child2 : public Basic {public: void say() { std::cout << "我是 child 2" << std::endl; }};int main(){ Basic* basePtr; basePtr = new Child1(); if (typeid(*basePtr) == typeid(Child2)) { std::cout << "[test 1]指针指向了 Child2 类型对象" << std::endl; } else { std::cout << "[test 1]指针没有指向 Child2 类型对象" << std::endl; } delete basePtr; basePtr = new Child2(); if (typeid(*basePtr) == typeid(Child2)) { std::cout << "[test 2]指针指向了 Child2 类型对象" << std::endl; } else { std::cout << "[test 2]指针没有指向 Child2 类型对象" << std::endl; } delete basePtr;}
运行后果,和刚刚应用 dynamic_cast
一样。咱们这里是来判断基类指针是否指向了某个具体类对象,typeid
当然也能够用来判断两个指针指向的具体类类型是否雷同,这里不再开展。
2 不容许应用 RTTI
出于某些起因,你的我的项目可能禁用了 RTTI,那这个时候咱们用怎么判断基类指针指向的具体类呢?咱们还能利用多态自身,就是给基类新增一个虚办法,子类在必要的时候来重写。
上面咱们持续用刚刚的例子,一起看看代码吧。
#include <iostream>class Basic {public: virtual void say() { std::cout << "我是基类" << std::endl; } virtual bool isChild2() { return false; }};class Child1 : public Basic {public: void say() { std::cout << "我是 child 1" << std::endl; }};class Child2 : public Basic {public: void say() { std::cout << "我是 child 2" << std::endl; } bool isChild2() { return true; }};int main(){ Basic* basePtr; basePtr = new Child1(); if (basePtr->isChild2()) { std::cout << "[test 1]指针指向了 Child2 类型对象" << std::endl; } else { std::cout << "[test 1]指针没有指向 Child2 类型对象" << std::endl; } delete basePtr; basePtr = new Child2(); if (basePtr->isChild2()) { std::cout << "[test 2]指针指向了 Child2 类型对象" << std::endl; } else { std::cout << "[test 2]指针没有指向 Child2 类型对象" << std::endl; } delete basePtr;}
咱们新增了一个 isChild2()
的办法,用来判断该类是否是 Child2
类型,因为我这里只须要判断基类指针是否指向了 Child2
类型的对象,所以就间接减少了个 bool
返回值的接口进行判断了。在理论应用时,也能够返回枚举变量,别离对应例子中的三个类。
3 总结
当我的项目容许 RTTI
时,咱们能够应用 dynamic_cast
和 typeid
运算符来判断一个基类指针指向的具体对象类型;当禁用 RTTI
时,咱们就利用多态自身,为基类新增一个办法,用来获取类类型信息。