C++ 宏、范型和RTTI 浅析

【摘要】
       RTTI(Run-Time Type Identification)是面向对象程序设计中一种重要的技术。

现行的C++标准对RTTI已经有了明白的支持。

只是在某些情况下出于特殊的开发须要,我们须要自己编码来实现。本文介绍了一些关于RTTI的基础知识及其原理和实现,并分析比較三者是线上的差异与联系。


【正文】

RTTI 的需求

       和非常多其它语言一样,C++是一种静态类型语言。其数据类型是在编译期就确定的,不能在执行时更改。然而因为面向对象程序设计中多态性的要求,C++中的指针或引用(Reference)本身的类型,可能与它实际代表(指向或引用)的类型并不一致。

有时我们须要将一个多态指针转换为事实上际指向对象的类型。就须要知道执行时的类型信息。这就产生了执行时类型识别的要求。


C++对RTTI的支持

     C++提供了两个keywordtypeid和dynamic_cast,一个type_info来支持RTTI。

     dynamic_cast操作符:它同意在执行时刻进行类型转换,从而使程序可以在一个类层次结构安全地转换类型。dynamic_cast提供了两种转换方式。把基类指针转换成派生类指针,或者把指向基类的左值转换成派生类的引用。

见下例讲述:

void company::payroll(employee *pe) {
//对指针转换失败,dynamic_cast返回NULL
if(programmer *pm=dynamic_cast(pe)){
pm->bonus(); 
}
}void company::payroll(employee &re) {
try{
//对引用转换失败的话,则会以抛出异常来报告错误
programmer &rm=dynamic_cast(re);
pm->bonus();
}
catch(std::bad_cast){
}
}
         这里bonus是programmer的成员函数,基类employee不具备这个特性。

所以我们必须使用安全的由基类到派生类类型转换,识别出programmer指针。


      typeid操作符:它指出指针或引用指向的对象的实际派生类型。


       比如:
employee* pe=new manager;
typeid(*pe)==typeid(manager) //true 
       typeid能够用于作用于各种类型名,对象和内置基本数据类型的实例、指针或者引用,当作用于指针和引用将返回它实际指向对象的类型信息。typeid的返回是type_info类型。


       type_info类:这个类的确切定义是与编译器实现相关的。以下是《C++ Primer》中给出的定义(參考资料[2]中谈到编译器必须提供的最小信息量):
class type_info {
private:
type_info(const type_info&);
type_info& operator=( const type_info& );
public:
virtual ~type_info();
int operator==( const type_info& ) const;
int operator!=( const type_info& ) const;
const char* name() const;
};

详见:http://bbs.csdn.net/topics/40414128


三者比較:

宏能够在编译前用字符替换的办法展开,减轻程序猿反复编码的工作量。



c++的范型(模版)也是在编译时确定终于的程序样式。他用的办法是编译时确定类型信息。

RTTI是执行期类型信息,能够在执行时得到对象的类型信息。

我们考察一个程序,为了说明问题,我特意找了一个简单的程序,这个程序比較a和b,假设a大于b就交换他们。可是。a、b的类型并不确定可能是字符串也可能是整数还可能是复数。为了简洁和不至于造成过的混淆,我不使用操作符重载。假定不论什么操作都是基于对象方法的,为了完毕这个函数。a、b必须支持compare(比較)和swape(交换)方法。

我们不清楚a、b的类型,假设有3种类型我们就必须写3个函数吗?那太累人了。我们用来定义这个函数好了。
#define exchange(a,b) if(a.compare(b))a.swape(b);
这样我们在使用的时候就能够直接使用exchange宏来表达这个函数,并且能够适应各种类型,仅仅要他们都支持这两个函数就能够。



我们还能够用范型来实现这个函数
template<typename T>
void exchange(T &a, T &b)
{
  if(a.compare(b))a.swape(b)
}
我们相同达到了目的。

假设编译器支持丰富的RTTI,我们还能够用脚本语言的方式来实现他们。
exchange(a, b)
{
  if(a.compare(b))a.swape(b);
}

上面三种方式区别在哪里呢?
第一种。使用字符替换的方式,在编译前展开宏,使之成为程序中的一段代码。


另外一种,在编译时。确定调用函数的參数的类型,并自己主动生成一个这个类型的暂时函数。
第三种。在执行时依据參数的类型确定是否可以执行这段程序。
前2种都是在编译时确认的。第三种是在执行时确定的。
在程序设计中有着大量的反复代码,我们须要一种方法来提高效率,可是为什么看着结构类似的程序须要反复代码呢?最重要的原因是,传统的程序中实体(函数、变量、属性等等)在编译时都须要内存中一块确定的地址(或者相对基址的偏移)来指代。这时cpu处理方式的内在要求,而这个和程序设计时依照名字引用的思维习惯是不一致的。
我们能够用宏和模版来取代手工对每一个类型的特化,可是在程序中仍然是使用地址来指代实体的。实际上每一个类型的特化程序依旧存在,仅仅是在程序设计外观上不可见了。
第三种方式。使用RTTI。程序中的实体都不再是确定的地址来指代,而是通过字符串名称(或者实体表)来指代,在执行时依据名称来特化。这样的方法和前2个方法是本质的不同。


使用RTTI能够在非常大程度上减轻宏和静态模版带来的副作用,使程序具有更加优雅的外观。
可是。C++的宏和静态模版也不是一无是处。他用在对效率更加严格场合是很合适的。

转载于:https://www.cnblogs.com/llguanli/p/8286541.html

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

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

相关文章

宝塔面板解决跨域

1、找到宝塔面板配置nginx文件的地方 2、增加如下代码 add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods POST,PUT,GET,DELETE; add_header Access-Control-Allow-Headers version, access-token, user-token, Accept, apiAuth, User-Agent…

Android 底部上拉抽屉view

module链接&#xff1a;https://download.csdn.net/download/meixi_android/10839835 接入module方法&#xff1a;https://blog.csdn.net/meixi_android/article/details/84655666 1、activity实现步骤 layout文件布局——DrawerLayoutContent_ID是抽屉内容id&#xff0c;dra…

前端学习(2693):重读vue电商网站14之步骤条的使用与美化

以下就是步骤条使用的核心代码&#xff0c;其中 active 绑定的是每一个 step 的下标&#xff0c;默认从 0 开始。其次&#xff0c;我们可以设置 aligin-center 属性来让我们的步骤条进行居中。el-step就是每一个步骤进度。 Javascript <!-- 步骤条区域 --> <el-steps…

启动项目时,运行其他方法

最近转学java&#xff0c;记录点滴 Component public class Test implements ApplicationRunner {Overridepublic void run(ApplicationArguments args) throws Exception {System.out.println("1234");} }主要是继承ApplicationRunner,并实现run方法

BOM--window对象

BOM 的核心对象是window&#xff0c;它表示浏览器的一个实例。在浏览器中&#xff0c;window 对象有双重角色&#xff0c;它既是通过JavaScript 访问浏览器窗口的一个接口&#xff0c;又是ECMAScript 规定的Global 对象。这意味着在网页中定义的任何一个对象、变量和函数&#…

Android 固定式底部上滑抽屉view

1、自定义view BottomDrawerLayout /*** 作者&#xff1a;created by meixi* 邮箱&#xff1a;15913707499163.com* 日期&#xff1a;2018/12/10 11*/public class BottomDrawerLayout extends ViewGroup {private static final String TAG "BottomDrawerLayout";p…

前端学习(2694):重读vue电商网站15之阻止页签tabs切换

主要函数如下&#xff1a; 在我们的 tabs 标签页添加一个 before-leave 函数 然后在 methods 中定义&#xff0c;根据第一个标签页的逻辑来阻止标签页的切换。

vscode中vue3项目vetur报错

vue3支持的插件应该是volar&#xff0c;之前有老的vue2项目插件vetur&#xff0c;所以会报错。 解决办法&#xff1a;在项目中新建.vscode, 新建文件settings.json,里面内容如下&#xff1a; {"vetur.validation.template": false,"vetur.validation.script&qu…

Android 侧滑多层view显示

侧滑module链接&#xff1a;https://download.csdn.net/download/meixi_android/10840271 引用方法 只需layout布局文件引用即可——第一个LinearLayout是底层view&#xff0c;第二个LinearLayout是上层view&#xff0c;侧滑即可显示底层view <com.daimajia.swipe.SwipeL…

MySQL的btree索引和hash索引的区别

MySQL的btree索引和hash索引的区别ash 索引结构的特殊性&#xff0c;其检索效率非常高&#xff0c;索引的检索可以一次定位&#xff0c;不像B-Tree 索引需要从根节点到枝节点&#xff0c;最后才能访问到页节点这样多次的IO访问&#xff0c;所以 Hash 索引的查询效率要远高于 B-…

vue2 vite antd vue 配置组件按需加载

1、安装vite-plugin-importer 2、配置vite.config.ts文件 import { fileURLToPath, URL } from url import { defineConfig } from vite import vue from vitejs/plugin-vue import vueJsx from vitejs/plugin-vue-jsx import usePluginImport from vite-plugin-importerexpo…

Android QQ登录集成

1、首先到腾讯开放平台创建应用&#xff0c;并上线——使用有效APP ID才可以进行qq登录 腾讯开放平台&#xff1a;https://open.tencent.com/ 上线后APP ID 2、下载腾讯sdk&#xff1a;https://download.csdn.net/download/meixi_android/10842092 3、activity代码详情&#…

前端学习(2695):重读vue电商网站16之Upload 上传组件

通过点击或者拖拽上传文件 Js <!-- action表示图片上传后台api地址 --> <el-upload:action"uploadURL":on-preview"handlePreview":on-remove"handleRemove"list-type"picture" ><el-button size"small" typ…

ajax 跨域

ajax跨域的原理 ajax出现请求跨域错误问题,主要原因就是因为浏览器的“同源策略”,可以参考 浏览器同源政策及其规避方法 CORS请求原理 CORS是一个W3C标准&#xff0c;全称是"跨域资源共享"&#xff08;Cross-origin resource sharing&#xff09;。它允许浏览器向跨…

vue3使用process报错Uncaught ReferenceError: process is not defined

我习惯于在config中根据process判断打包状态。这次升级到vue3遇到报错。 解决方案&#xff0c;还是配置一下vite.config.json。 增加如下配置即可。 export default defineConfig({// ...define: {process.env: process.env} })

左右滑动实现activity之间的跳转

首先来看一下实现效果 1. BaseActivity extends Activity 首先&#xff0c;由于activity类之间存在很多共性 &#xff0c;比如跳以及滑动等事件&#xff0c;所以需要抽象出一个父类来&#xff0c;简化代码量。 附代码&#xff1a; /*** 按照1、2、3的步骤走* / public abstr…

vue3 watch props 监听属性变化

我的需求是弹出一个模态框。使用visible控制隐藏与现实&#xff0c;需要watch&#xff0c;visible变化&#xff0c;执行其他相关操作。 核心代码如下&#xff1a; import { watch, toRefs } from "vue"; const props defineProps({visible: {type: Boolean,default…

Android 调用原生API获取地理位置和经纬度,判断所在国家

public static boolean isCN(Context context) {TelephonyManager tm (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);String countryIso tm.getSimCountryIso();boolean isCN false;//判断是不是大陆if (!TextUtils.isEmpty(countryIso)) {count…

3 View - 状态保持 session

1.状态保持 http协议是无状态的&#xff1a;每次请求都是一次新的请求&#xff0c;不会记得之前通信的状态客户端与服务器端的一次通信&#xff0c;就是一次会话实现状态保持的方式&#xff1a;在客户端或服务器端存储与会话有关的数据存储方式包括cookie、session&#xff0c;…