RTTI: dynamic_cast typeid

dynamic_cast:将基类类型的指针向派生类指针安全转换。多用于下行转换。上行转换时,和static_cast是一样的。C++类型转换看这里。而const_cast用来修改类型的const或volatile属性。。。下面主要说多态下的RTTI:

使用条件:
  基类应有虚函数
  编译器需启用Runtime Type Information/Identification(RTTI),运行时类型信息。VS下在项目属性页下启用,如下,选 : (VS2013测试:默认的留不选也能正常使用dynamic_cast)

结果:
  对指针进行dynamic_cast,失败返回null,成功返回正常cast后的对象指针; //首选
  对引用进行dynamic_cast,失败抛出一个异常std::bad_cast,成功返回正常cast后的对象引用。

用处:一般用在多态中,即基类的指针或引用指向派生类对象时,进行安全的向下转换。
   多态的基本例子如下:

#include <iostream>
using namespace std;class Base
{
public:virtual void vFoo(){cout << "Base::vFoo()" << endl;}virtual ~Base(){ }
};class Derived : public Base
{
public:    virtual void vFoo(){cout << "Derived::vFoo()" << endl;}void Other(){cout << "Derived::Other()" << endl;}virtual ~Derived(){ }
};
int main()
{Base *pBase = new Derived;pBase->vFoo(); //"Derived::vFoo()"delete pBase;return 0;
}
多态举例

可看到,由于基类指针实际指向派生类实例,所以实际调用的是派生类里的vFoo()函数。

但尝试 pBase->Other();//编译出错 ,虽然pBase指向派生类实例,但指针本身的类型却决定了它所能调用的函数范围。

强制转换下呢: ((Derived*)pBase)->Other();//"Derived::Other()" ,成功了。

但如果这样呢:

Base *pBase = new Base;
((Derived*)pBase)->Other();

仍然成功了,正确输出。实际Other函数可以直接使用Derived::Other()来调用的,因为函数里没交互数据成员。但如果交互了呢:

//Derived类里:
public:int a = 6;//C++11void Other(){cout << a << endl;//随机数字,如73756547a = 100;}
};//main里
Base *pBase = new Base;
((Derived*)pBase)->Other();

运行后竟仍然成功了。但从输出的a是随机数字可以看出int a =6;并没有执行过,因为new的是Base实例。这里我们虽依然运行成功,但从程序上看,这是不对的。如果Derived类封装的更复杂(比如在构造函数里new, Other里delete),可能肯定运行时会崩溃!所以c语言形式的强制转换在此种情况下是不安全的。

可能问,为什么一定要调用Other函数呢,如果一定要在使用多态时调用它,在基类里添加Other函数并声明它为虚函数不就行了。是的,这样可以。但有时,比如我使用了一个第三方库(封装到lib里,只提供了它的头文件),我在从它继承的派生类中添加了新函数,并想在多态下使用。此时是不可能在第三方库里添加虚函数的,因为库不能重新编译。这时就需要安全的类型转换了。dynamic_cast就可以在此种情况下起作用:

void foo(Base *p)
{Derived* pChild = dynamic_cast<Derived*>(p);if(pChild)pChild->Other();else{//不指向派生类实例时的处理
    }
}
//引用时
void foo2(Base &b)
{try{Derived& pChild = dynamic_cast<Derived&>(b);pChild.Other();}catch(std::bad_cast){//不指向派生类实例时的处理
    }
}


对基类指针,不属于某个派生类实例将返回null,这样可用来判断类型:

void foo(Base *p)
{if(dynamic_cast<Derived1*>(p)){//属于Derived1类
    }else if(dynamic_cast<Derived2*>(p)){//属于Derived2类
    }//...
}

 


typeid:

http://www.cppblog.com/smagle/archive/2010/05/14/115286.aspx这篇写的很好,我就不怎么写了。。。直接贴它的几个例子吧

需要注意(仍根据上文例子):

Base *pBase = new Derived;
cout << typeid(pBase).name() << endl; //class Base*   虽然基类有虚函数且指针指向派生类对象,但仍输出指针类型本身
cout << typeid(*pBase).name() << endl;//class Derived
Base &rD = *pBase;//class Derived   //注意,引用和指针结果不同

//所以typeid能对含虚函数的类类型(对象本身或引用)判断出其指向的对象的类型信息,对指针无用。

例子:

#include <iostream>
using namespace std;class Base
{
};
class Derived: public Base
{
};void foo()
{
}
int main()
{Base b, *pb;pb = NULL;Derived d;cout << typeid(int).name() << endl<< typeid(unsigned).name() << endl<< typeid(long).name() << endl<< typeid(unsigned long).name() << endl<< typeid(char).name() << endl<< typeid(unsigned char).name() << endl<< typeid(float).name() << endl<< typeid(double).name() << endl<< typeid(string).name() << endl << endl//函数类型和函数指针也可以<< typeid(void (*)(int, int)).name() << endl<< typeid(foo).name() << endl << endl<< typeid(Base).name() << endl<< typeid(b).name()<<endl<< typeid(pb).name()<<endl //虽然指向NULL,但本身类型是Base *<< typeid(Derived).name() << endl<< typeid(d).name()<<endl<< typeid(type_info).name() << endl;return 0;
}
基本类型与一般类

 

#include <iostream>
using namespace std;class Base
{
public:virtual void foo(){}virtual ~Base(){}
};
class Derived: public Base
{
};int main()
{Base *pd = new Derived;cout << typeid(pd).name() << endl //class Base *<< typeid(*pd).name() << endl;Base &rD = *pd;cout << typeid(rD).name() << endl;//虽无法从指针本身上判断,但可对其解引用if(typeid(Derived) == typeid(*pd))cout << "类型相同" << endl;elsecout << "类型不同" << endl;delete pd;return 0;
}

转载于:https://www.cnblogs.com/sfqtsh/p/5143678.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/419105.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

20 | 散列表(下):为什么散列表和链表经常会一起使用?

有两种数据结构&#xff0c;散列表和链表经常会被放在一起使用。常见的使用方式有&#xff1a; 用链表来实现 LRU 缓存淘汰算法&#xff0c;链表实现的 LRU 缓存淘汰算法的时间复杂度是 O(n)&#xff0c;通过散列表可以将这个时间复杂度降低到 O(1)。Redis 的有序集合是使用跳…

冬季小学期 NIIT公司 web前端培训 CSS

外边距合并 http://www.w3school.com.cn/css/css_margin_collapsing.asp div、p、h1 块元素 span行元素 浮动&#xff1a;浮动的框可以向左或向右移动&#xff0c;直到它的外边缘碰到包含框或另一个浮动框的边框为止。 http://www.w3school.com.cn/css/css_positioning_floatin…

BZOJ 1491: [NOI2007]社交网络( floyd )

floyd...求最短路时顺便求出路径数. 时间复杂度O(N^3) -------------------------------------------------------------------------------------------#include<cstdio>#include<algorithm>#include<cstring>using namespace std;typedef long long ll;con…

前端学习(1731):前端系列javascript之发布窗口布局下

index.vue <template><view class"content"><view class"todo-header"><view class"todo-header_left"><text class"active-text">全部</text><text>10条</text></view><v…

Ace教你一步一步做Android新闻客户端(三) JSON数据解析

对于服务器端来说&#xff0c;返回给客户端的数据格式一般分为html、xml和json这三种格式&#xff0c;现在给大家讲解一下json这个知识点&#xff0c; 1 如何通过json-lib和gson这两个json解析库来对解析我们的json数据&#xff0c; 2 以及如何在我们的Android客户端解析来自服…

前端学习(1732):前端系列javascript之插入内容

index.vue <template><view class"content"><view v-if"list.length!0" class"todo-header"><view class"todo-header_left"><text class"active-text">全部</text><text>10条&…

前端学习(1732):前端系列javascript之状态切换

index.vue <template><view class"content"><view v-if"list.length!0" class"todo-header"><view class"todo-header_left"><text class"active-text">全部</text><text>10条&…

记一次若依ruoyi-ui(Vue2) 关闭tab页并打开新页面

网上教程很多&#xff0c;但是都是给前端代码段&#xff0c;都不知道怎么使用&#xff08;本人菜鸟一个&#xff09;&#xff0c;今天记一次完整的&#xff1a; 在你需要关闭的tab页面&#xff0c;加入以下代码&#xff1a; handleCommit()是我需要关闭页面的方法&#xff0c…

log4j自定义配置文件(SpringMVC项目)

问题来源 本周在实际项目中发现无法自定义的log4j-dev配置的error日志级别文件无法生效&#xff0c;项目启动后仍然采用默认的info级别日志进行打印。之所以自定义名称&#xff0c;是为了减少隔离不同环境的日志级别&#xff0c;比如开发dev环境使用debug、info级别&#xff0…

23 | 二叉树基础(上):什么样的二叉树适合用数组来存储?

思考题 二叉树有哪几种存储方式&#xff1f;什么样的二叉树适合用数组来存储&#xff1f; 树&#xff08;Tree&#xff09; 根节点&#xff1a;没有父节点的节点叶子节点或者叶节点&#xff1a;没有子节点的节点叫做 树的高度、深度、层&#xff1a; 举例说明&#xff1a; 生…

HBase 手动 flush 机制梳理

对应 HBase 版本0.94.1&#xff0c;对照了开源的版本和工作使用的某发行版 问题&#xff1a;在 HBase shell 里面输入 flush table_or_region_name之后&#xff0c;发生了什么&#xff1f;具体的实现是怎么样的&#xff1f;对于现有的某个表&#xff0c;我如何在做操作之前估算…

从0到1搞一波dubbo

1、为什么需要dubbo&#xff1f;&#xff08;为了解决什么问题&#xff1f;&#xff09; 架构演变 1 单一应用架构 2 应用和数据库单独部署 3 应用和数据库集群部署 4 数据库压力变大&#xff0c;读写分离 5 使用缓存技术加快速度 6 数据库分库分表 7 应用分为不同的类型拆分 …

前端学习(1734):前端系列javascript之添加动画

<template><view class"content"><!-- 状态栏 --><view v-if"list.length ! 0" class"todo-header"><!-- 状态栏的左侧 --><view class"todo-header__left"><text class"active-text&quo…

android146 360 病毒查杀

<?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"match_parent"android:orientatio…

24 | 二叉树基础(下):有了如此高效的散列表,为什么还需要二叉树?

这节学习一种特殊的二叉树—二叉查找树。它最大的特点是支持动态数据集合的快速插入、删除、查找操作。但是散列表也是支持这些操作的&#xff0c;并且散列表的这些操作比二叉查找树更高效&#xff0c;时间复杂度是 O(1)。 问题引入 既然有高效的散列表&#xff0c;二叉树的地…

25 | 红黑树(上):为什么工程中都用红黑树这种二叉树?

问题引入 二叉查找树在频繁的动态更新过程中&#xff0c;可能会出现树的高度远大于 log2n 的情况&#xff0c;从而导致各个操作的效率下降。极端情况下&#xff0c;二叉树会退化为链表&#xff0c;时间复杂度会退化到 O(n)。要解决这个复杂度退化的问题&#xff0c;需要设计一…