member function相对于nonmember function之间不存在效率之间的差别,因为编译器内部已经将“member 函数实体”转化为对等的“nonmember 函数实体”,转化过程:

1.改写函数的函数原型,以安插一个额外的参数到member function中,用于提供一个存储管道,使class object得以调用该函数,该额外参数被称为this指针。

2.将每一个“对nonstatic data member的存取操作”改为经由this指针来存取

3.将成员函数重新写成一个外部函数,对函数名称进行“mangling”处理,使它在程序中成为独一无二的词汇。

名字的特殊处理(name mangling)

一般而言,member的名字前会被加上class 的名称,形成独一无二的命名,例如:

Class Bar{public: int val;}
class Foo:public Bar{public int val;}
//Foo 的内部
class Foo
{
public :
int ival_3bar;
int ival_3foo;
}

不管要处理哪一个ival,都可以通过“name mangling”,都可以绝对清楚地指出来,由于member functions可以被重载化(overloaded),所以需要更广泛的mangling手法,以提供绝对独一无二的名称。


Virtual Member Funtion(虚成员函数)

如果normalize()是一个virtual member function,那么

ptr->normalize();
//将会被内部转化为下面这样
(*ptr->vptr[1])(ptr);

1.vptr表示由编译器产生的指针,指向virtual table,它被安插在每一个“声明有或继承自一个或多个virtual functions”的class object中,事实上其名称也会“mangled”,因为在一个复杂的class派生体系中,可能会存在多个vptrs。

2.1是virtual table slot的索引值,关联到normailize()函数

3.第二个ptr表示this指针。

为了支持virtual function机制,必须首先能够对多态对象有某种形式的“执行器类型判断法”,在C++中,多态表示“以一个public base class 指针(或reference)寻址出一个derived class object”的意思。

多态又可分为passive(消极)active(积极)

Point*ptr=new Point2d;
ptr=new Point3d;//这是消极的

ptr的多态机能主要扮演一个传送机制的角色,经由它,我们可以在程序的任何地方采用一组public derived类型,这种多态形式被称为是passive的,可以在编译期完成(virtual base class除外

当被指出的对象真正被使用时,多态也就变成了active的了,下面对于virtual function的调用

ptr->z();//active的多态

其中z()是一个virtual function,那么什么信息才能让我们在执行期调用正确的z()实体?我们要知道:

1.ptr所指对象的真实类型,这可使我们选择正确的z()实体。

2.z()实体位置,以便我们能够调用它。

在实现上,可以在每一个多态的class object身上添加两个members

1.一个字符串或者数字,表示class 的类型。

2.一个指针,指向某表格,表格中带有程序的virtual functions的执行期地址。

表格中的virutual functions地址如何被构建的?

在编译时期(由上面的ptr=new Point3d可知)就可以获得virtual function的地址,此外,这一组地址是固定不变的,执行期不可能新增或替换之,由于程序执行时,表格的大小和内容都不会发生改变,所以其构建和存取都可以由编译器完全掌握,不需要执行器的任何接入。


上面是准备好地址,接下来是如何寻址:

1.为了找到表格,每一个class object被安插上一个由编译器内部产生的指针,指向该表格。

2.为了找到函数地址,每一个virutual function被指派一个表格索引值。

执行期要做的,只是在特定的记录着virutual function的地址激活virutal function


一个class只会有一个virtual table,每一个table内涵其对应的class object中所有的active virtual functions函数实体的地址,这些active virtual function包括:

1.这个class 所定义的函数实体,它会改写(overriging)一个可能存在的base class virtual function函数实体。

2.继承自base class的函数实体,这是在derived class决定不该写virtual function时才会出现的情况。

3.一个pure_virtual_called()函数的实体。

每一个virtual function都派有一个固定的索引值,这个索引值在整个继承体系中与特定的virtual function相关联。



当B继承自A的时候:

1.它可以继承base class所声明的virutal functions的函数实体,正确的说,是该函数实体的地址会被拷贝到派生类的virtual table相对应的slot中

2.它可以使用自己的函数实体,这表示它自己的函数实体地址必须放在对应的slot中。

3.它还可以加入一个新的virtual function。这时候virtual table的尺寸会增大一个slot,而新的函数实体地址也会放进该slot中。

如果我有这样一个函数调用:

ptr->z();

那么我怎么拥有足够多的只是在编译时期设定virtual function调用呢?

1.一般而言,我们不知道ptr所指对象的真正类型,但是我们可以知道,经由ptr可以存取到该对象的virtual table;

2.虽然我们不知道哪一个z()会被调用,但我知道每一个z()函数的地址都被放在slot4中,这样我们就可以转化为:

(*ptr->vptr[4])(ptr);

以上只包含单继承


多继承下的虚函数



(图太难画了,我直接截书里的图吧)

在多重继承下,一个派生类内涵n个virtual table,n是上一层base classes的数目,针对每一个virtual tables,derived对象中有对应的vptr。

《深度探索C++对象模型》第四章 Function语意学的更多相关文章

  1. 深度探索C++对象模型第四章:函数语义学

    C++有三种类型的成员函数:static/nonstatic/virtual 一.成员的各种调用方式 C with class 只支持非静态成员函数(Nonstatic member function ...

  2. 《深度探索C++对象模型》第二章 | 构造函数语意学

    默认构造函数的构建操作 默认构造函数在需要的时候被编译器合成出来.这里"在需要的时候"指的是编译器需要的时候. 带有默认构造函数的成员对象 如果一个类没有任何构造函数,但是它包含一 ...

  3. Android深度探索-卷1第四章心得体会

    这一章的和三章的git用法有联系,so,吧上一章的git基本用法搞好了再来,具体的方法就是看书上网查,这里就不做详细步骤介绍了.这章就有点意思了,是源码的下载和编译,有能看的,能自己鼓捣的,本章介绍的 ...

  4. 深度探索C++对象模型之第一章:关于对象之C++对象模型

    一.C和C++对比: C语言的Point3d: 数据成员定义在结构体之内,存在一组各个以功能为导向的函数中,共同处理外部的数据. typedef struct point3d { float x; f ...

  5. 深度探索C++对象模型之第一章:关于对象之对象的差异

    一.三种程序设计范式: C++程序设计模型支持三种程序设计范式(programming paradiams). 程序模型(procedural model) char boy[] = "cc ...

  6. 深度探索C++对象模型之第二章:构造函数语意学之Copy constructor的构造操作

    C++ Standard将copy constructor分为trivial 和nontrivial两种:只有nontrivial的实例才会被合成于程序之中.决定一个copy constructor是 ...

  7. 深度探索C++对象模型之第二章:构造函数语意学之Default constructor的构造操作

    C++新手一般由两个常见的误解: 如果任何class没有定义默认构造函数(default constructor),编译器就会合成一个来. 编译器合成的的default constructor会显示的 ...

  8. 【C++对象模型】第四章 Function 语意学

    1.Member的各种调用方式 1.1 Nonstatic Member Functions 实际上编译器是将member function被内化为nonmember的形式,经过下面转化步骤: 1.给 ...

  9. 深度探索C++对象模型之第二章:构造函数语意学之成员初始值列表

    当我们需要设置class member的初值时,要么是经过member initialization list ,要么在construcotr内. 一.先讨论必须使用member initializa ...

随机推荐

  1. hyperf-搭建初始化

    官方文档* https://hyperf.wiki/2.0/#/README 初步搭建1. 安装项目 composer create-project hyperf/hyperf-skeleton 2. ...

  2. 成功 解决 @keyup.enter=“search()“ 在el-input 组件中不生效的问题

    1.问题描述 在输入框中.输入内容.点击回车.没有效果 问题代码 2.问题解决思路 查看官网的解释说明: 要使用.native修饰符 3.问题解决 修改后的代码 修改后的效果

  3. 盘它!基于CANN的辅助驾驶AI实战案例,轻松搞定车辆检测和车距计算!

    摘要:基于昇腾AI异构计算架构CANN(Compute Architecture for Neural Networks)的简易版辅助驾驶AI应用,具备车辆检测.车距计算等基本功能,作为辅助驾驶入门级 ...

  4. vuex和浏览器【sessionStorage,localStorage ..】 得区别

    1.Vuex的主要作用是用来共享和管理数据,那为什么不直接使用浏览器缓存呢?2.Vuex和浏览器缓存策略的根本区别是什么?3.Vuex,state仓库中的数据流是单向同步的,那为什么可以在action ...

  5. MQTT GUI 客户端 可视化管理工具推荐

    一款好用的 MQTT 客户端工具可以极大地提高开发者使用MQTT的效率.MQTT 客户端工具常用于建立与 MQTT 服务器的连接,进行主题订阅.消息收发等操作. 今天,在此推荐一款优秀的MQTT GU ...

  6. 陪你去看 Lodash.js 起步

    lodash 起步(数组) Lodash 是一个较为流行的 JavaScript 的实用工具库. 在开发过程中如果能熟练使用一些工具库提供的方法,有利于提高开发效率. 笔者从 API 上入手,不分析其 ...

  7. Git+Github协作开发流程

    采用合作开发者的方式进行协作开发,下面以zlcook和zlserver合作开发iqas-ios-record项目为例进行讲解,zlcook为项目创建者,zlserver为项目参与者.协作开发步骤如下: ...

  8. 10、数组a和b各有10个元素。将他们相同的位置元素逐个比较, 如果a中元素大于b中对应元素的次数多于b数组中元素大于a中元素的次数, 则认为a大于b。请统计大于等于小于的次数

    /* 数组a和b各有10个元素.将他们相同的位置元素逐个比较, 如果a中元素大于b中对应元素的次数多于b数组中元素大于a中元素的次数, 则认为a大于b.请统计大于等于小于的次数 */ #include ...

  9. c#入参使用引用类型为啥要加ref?

    摘一段来自官网的说明 :方法的参数列表中使用 ref 关键字时,它指示参数按引用传递,而非按值传递. ref 关键字让形参成为实参的别名,这必须是变量. 换而言之,对形参执行的任何操作都是对实参执行的 ...

  10. BFS和DFS学习笔记

    1 算法介绍 1.1 BFS Breadth First Search(广度优先搜索),将相邻的节点一层层查找,找到最多的 以上图为例,首先确定一个根节点,然后依次在剩下的节点中找已找出的节点的相邻节 ...