深度探索C++对象模型--读书笔记

深度探索C++对象模型

第一章:关于对象

封装之后的布局成本

  • C++在布局以及存取时间上主要的额外负担是由virtual引起

    • 1、VIrtual function机制:用以支持一个有效率的“执行期绑定”(runtime binding)
    • 2、virtual base class:用以实现“多次出现在集成体系中的base,有一个单一而被共享的实例”

1、C++对象模式

  • 简单对象模式

    • 成员都不在对象中:一个对象都有一些列的slots,每个slot指向一个member,member按照声明顺序,各被指定一个slot
  • 表格驱动对象模型

    • 将members抽象出来,分化成data member table和function member table,类对象中含有分别指向这两个表的指针,member function table中是一些列的slots,每个slot指向一个member function;data member table则直接持有Data本身。
  • C++对象模型

    • Nonstatic data member被配置在每一个class object之内,static data member则放在个别的class object之外

    • static 和 nonstatic function members被放在个别的class object之外

    • virtual function

      • 1、每个class产生出一堆指向virtual functions的指针,放在表格中,被称为virtual table(vtbl)

      • 2、每个class object被安插一个指针,指向相关的virtual table。通常该指针被称为vptr。vptr的setting和resseting都由每个类的constructor、destructor和copy assignment运算符完成。

        • 每个class所关联的type_info object(用以支持runtime type identification,RTTI),也经由virtual table被指出来,通常放在表格的第一个slot

2、关键词带来的差异

  • 关键字带来的困扰

    • 使用class或struct关键字是否可以给予“类型的内部声明”以某种承诺。即若struct关键字的使用是实现了C的数据抽象概念;而class关键字实现的C++的ADT(abstract Data Type)观念,这种不一致性就是一种错误的语言语法
  • 策略正确的struct

    • C Struct在C++中第一个合理用途就是当你传递“一个复杂的class object的全部或部分”到某个C函数去时,struct声明可以将数据封装起来,并保证拥有与C兼容的空间布局(z这种保证旨在组合composition的情况下存在),如果是继承,不是组合,编译器会决定是否应该有额外的data members被安插到base struct object之中

3、对象的差异

  • C++程序设计模型直接支持三种程序设计范式(programming paradigms)

    • 程序模型(procedural model)

    • 抽象数据类型模型(abstract data type model,ADT)

      • 所谓的“抽象”是和一组表达式(public接口)一起定义
    • 面向对象模型(object-oriented model)

      • 此模型中有一些彼此相关的类型,通过一个抽象的base class(用以提供共同接口)被封装起来

      • 支持多态的场景

        • 1、经过一组隐式的转换操作,如把派生类指针转换成基类指针

        • 2、经由virtual function机制

        • 3、经由dynamic_cast和typeid运算符

          • if(circle pc = dynamic_cast<circle>(ps)) ....
        • 多态的主要用途:仅有一个共同的接口来影响类型的封装,这个接口通常被定义在一个抽象的base class中。

      • class object 的大小

        • 1、其nonstatic data members的总和大小

        • 2、加上任何由于alignment的需求而填补(padding)上去的空间

          • alignment就是将数值调整到某数的倍数,在32位计算机上,通常alignment为4bytes(32位),以使bus的“运输量”达到最高效率
        • 3、加上为了支持virtual而由内部产生的任何额外负担

第二章:构造函数语意学

1、Default Constructor的构造操作

C++ Annotated Reference Mannual(ARM)
什么时候才会合成一个default constructor?
当编译器需要它的时候。
对于class X,如果没有任何user-declared constructor,那么会有一个default constructor被隐式(implicitly)声明出来,一个被隐式声明出来的default constructor将是一个trivial(浅薄无能的,没啥用的)constructor

  • 带有default constructor的member class object

    • 在C++不同的编译模块中(不同的编译模块意指不同的文件),编译器如何避免合成多个default constructor?解决办法就是把合成的default constructor、copy constructor、destructor、assignment copy operator都以inline方式完成。一个inline函数有静态连接(static linkage),不会被文件以外者看到
    • 被合成的default constructor只是满足编译器的需要,并不满足程序的需要
  • 带有default constructor 的base class

    • 如果一个没有任何constructors的class派生自一个“带有default constructor”的base class,那么这个derived class的default constructor会被视为nontrivial,并因此需要被合成出来
  • 带有一个virtual function的class

    • 另外两种情况也需要合成default constructor

      • 1、class声明(或继承)一个virtual function
      • 2、class派生自一个继承串链,其中有一个或更多的virtual base classes
  • 带有一个virtual base class 的class

  • 小结

    • 上述四种情况,会造成“编译器必须为未声明constructor的classes合成一个default constructor”,C++标准将这些合成物称为implicit nontrivial default constructors,被合成出来的constructor只能满足编译器而非程序的需要,
    • 它之所以能够完成任务,是借着“调用member object或base class的default constructor”或是“为每一个object初始化其virtual function机制或virtual base class机制”而完成
    • 在合成的default constructor中,只有base class subobjects和member class objects会被初始化;所有其他的nonstatic data member(如整数、整数指针、整数数组等等)都不会被初始化
    • 误解:1、任何class如果没有定义default constructor,就会被合成一个(需要的时候合成);2、编译器合成出来的default constructor会显示设定“class内每一个data member的默认值”:(只满足编译器的需要)

2、Copy Constructor的构造操作

  • default memberwise initialization

    • 当class object以“相同class的另一个object”作为初始值,其内部是以所谓的default memberwise initialization手法完成,即把每一个内建的或派生的data member(如指针或一个数组)的值,从某一个object 拷贝一份到另一个object身上,以递归的方式实现memberwise initialization
    • 一个class object可用copy constructor和copy assignment operator两种方式复制得到
  • bitwise copy semantics(位逐次拷贝)

  • 不要bitwise copy semantics

    • 一个class不展现出“bitwise copy semantics”的四种情况

      • 1、当class内含一个member object而后者的class声明中有一个copy constructor时(不管是显示声明还是被编译器合成的)
      • 2、当class继承自一个base class而后者存在一个copy constructor
      • 3、当class声明了一个或多个virtual function时
      • 4、当class派生自一个继承串链,其中有一个或多个virtual base classes时
  • 重新设定virtual table的指针

    • 只要有一个class声明一个或多个virtual function就会有如下操作:

      • 1、增加一个virtual function table(vtbl),内含每一个有作用的virtual function的地址
      • 2、一个指向virtual function table的指针(vptr),安插在每一个class object内
  • 处理virtual base class subobject

    • 一个class object如果以另一个object作为初值,而后者有一个virtual base class subobject,那么也会使“bitwise copy semantics”失效
    • 每一个编译器对于虚拟继承的支持承诺,都代表必须让“derived class object中的virtual base class subobject位置”在执行期就准备妥当。维护“位置的完整性”是编译器的责任。而“bitwise copy semantics”可能会破坏这个位置,因此编译器必须在自己合成出来的copy constructor中做出仲裁

3、程序转化语意学

  • 显式的初始化操作

    X x0;
    void foo_bar(){
    X x1(x0); //定义了x1
    X x2(x0); //定义了x2
    X x3(x0); //定义了x3
    }

    • 转化需要两个阶段

      • 1、重写每一个定义,其中的初始化操作会被剥除(定义:指“占用内存”的行为)
      • 2、class的copy constructor调用操作会被安插进去
  • 参数的初始化

    • 把一个class object当做参数传给一个函数(或作为一个函数的返回值),相当于 X xx = arg;的初始化操作,其中xx嗲表形式参数(或返回值),arg代表真正的参数值。

      • 它会引入一个临时性object,调用copy constructor将它初始化,然后将此临时object交给函数,实现初始操作
    • 另一种实现方法:“拷贝构造”(copy construct)把实际参数直接构建在其应该的位置上,此位置视函数活动范围的不同,记录于程序堆栈中。

  • 返回值的初始化

    • 双阶段转换

      • 1、首先加上一个额外参数,类型是class object的一个reference,这个参数将用来放置被”拷贝构造(copy construct)“而得的返回值
      • 2、在return指令之前安插一个copy constructor调用操作,以便将欲传回之object的内容当做上述新增参数的初值
  • 使用者层面做优化

  • 编译器层面做优化

  • copy constructor要还是不要

成员们的初始化队伍

  • 必须使用初始化列表的情况:

    • 1、当初始化一个reference member时
    • 2、当初始化一个const member时
    • 3、当调用一个base class的constructor,而它拥有一组参数时
    • 4、当调用一个member class的constructor,而它拥有一组参数时

第三章:Data语意学

data member的绑定

  • 一个inline member function躯体之内的一个data member绑定操作,会在整个class声明完成后发生

data member的布局

  • nonstatic data members在class object中的排列顺序和其被声明的顺序是一致的

  • static data members不会放在对象布局之中,而是程序的data segment中

  • C++ standard要求,在同一个access section(即private、public、private等区段中),members的排列只需符合“较晚出现的members在class object中有较高的地址”这一条即可

  • 编译器也会合成一些内部使用的data members,以支持整个对象模型

    • 如vptr,目前所有的编译器都把他安插在每一个“内含virtual function之class”的object内。传统上它会被放在所有显示声明的members的最后

data member的存取

  • static data members

    • 每一个static data member只有一个实例,存放在程序的data segment之中,每次程序取用static member时,就会被内部转换为对该唯一extern 实例的直接参考操作

    • static member的存取并不需要通过class object

    • 若取一个static data member的地址,会得到一个指向其数据类型的指针,而不是一个指向其class member的指针,因为static member并不含在一个class object之中


      &Point3d::chunkSize
      会得到const int *类型的指针

    • 如果两个类中,都声明了一个static member freelist,那么他们都会被放在程序的data segment时,就会产生冲突,那怎么办?

      • 编译器的解决办法是暗中对每一个static data member编码(被称为:name-mangling),以获得独一无二的程序识别代码

      • 任何name-mangling有两个重点

        • 1、一个算法,推导出独一无二的名称
        • 2、万一编译系统(或环境工具)必须和使用者交谈,那些独一无二的名称可以轻易被推倒回到原来的名称
  • nonstatic data members

    • nonstatic data member直接存放在每一个class object中,通过显示(explicit)或隐式(implicit) class object对其进行存取

    • 对nonstatic data member的存取,经由一个”implicit class object“(由this指针表达)完成

      Point3d
      Point3d::translate(const Point3d &pt){
      x += pt.x;
      y += pt.y;
      z += pt.z;
      }

      //member function内部转化
      Point3d
      Point3d::translate(Point3d *const this,const Point3d &pt){
      this->x += pt.x;
      this->y += pt.y;
      this->z += pt.z;
      }

    • 要对一个nonstatic data member进行村去操作,编译器需要把class object的起始位置加上data member的偏移位置(offset)

      如:
      origin._y= 0.0
      那地址&origin._y等于
      &origin + (&origin._y -1);

      -1操作是指向data member的指针,其offset值总是被加1,这样就可以使编译器区分出“一个指向data member的指针,用以指出class的第一个member”和“一个指向data member的指针,没有指出任何member”两种情况

“继承”与data member

  • 在C++继承模型中,一个derived class object所表现出来的东西,是其自己的members加上其base class(es) members的总和

  • 只要继承不要多态

    • 把一个class 分解成两层或更多层,有可能会为了“表现class体系之抽象化”而膨胀所需的空间
  • 加上多态

    • 会带来空间和存取时间上的额外负担

      • 1、导入了一个virtual table,用来存放它所声明的virtual function的地址。这个table的元素个数一般而言是被声明的virtual function的个数,再加上一个或两个slots(用以支持runtime type identification)

      • 2、在每一个class object中导入一个vptr,提供执行期的链接,使每一个object能够找到相应的virtual table

      • 3、加强constructor,使它能够为vptr设定初值,让它指向class所对应的virtual table

      • 4、加强destructor,使它能够抹消“指向class 之相关virtual table”的vptr

        • destructor的调用顺序是相反的:从derived class到base class
  • 多重继承

    • 多重继承的问题主要发生于derived class objects和其第二或后继的base class objects之间的转换,不论是直接转换还是经由所支持的virtual function机制做转换
    • 如果要存取第二个(或后继)base class中的一个data member,由于members的位置在编译时就固定了,因此存取members只是一个简单的offset运算。
  • 虚拟继承

    • 典型的一个例子是早期的iostream

      • 不论是istream还是ostream都内含一个ios subobject,然后再iostream的对象布局中,只需要单一一份ios subobject,语言层面的解决办法就是导入所谓的虚拟继承

      • class如果内含一个或多个virtual base class subobject,像iostream那样,将被分割成两部分,一个不变区域和一个共享区域。

        • 不变区域的数据,不管后继如何衍化,总是拥有固定的offset(以object的开头算起),所以这部分数据科技直接存取
        • 共享区域,所表现出来的就是virtual base class subobject这部分数据,其位置会因为每次的派生操作而有变化,所以他们只可以间接存取
    • 如何存取class的共享部分呢?

      • cfont编译器会在每一个derived class object中安插一些指针,每个指针指向一个virtual base class,要存取继承而来的virtual base class members,可以通过相关指针间接完成。
    • 一般而言,virtual base class最有效的一种运用形式就是,一个抽象的virtual base class,没有任何data members

对象成员的效率

指向data members的指针

  • C++ standard允许vptr被放在对象中的任何位置,通常编译器将其放在对象的头或尾

  • C++语言要求同一个access level中的members的排列顺序和其声明顺序相同

  • 如何区分“没有指向任何data member”的指针和一个指向“第一个data member”的指针?

    • 每一个真正的member offset值都被加1,因此,不论编译器或是使用者都必须记住,在真正使用该值以指出一个member之前,请先减掉1

第七章:站在对象模型的顶端

1、Template

  • Template的“实例化”行为

  • Template的错误报告

  • Template中的名称决议法

    • “scope of the template declaration”,用以专注于一般的template class
    • "scope of the template instantiation",用以专注于特定的实例
  • Member function的实例化行为

    • 至于template function的 实例化,目前编译器提供了两种策略

      • 1、一个是编译时期策略,程序代码必须在program text file中备妥可用
      • 2、另一个是链接时期策略,有一些meta-compilation工具可以导引编译器的实例化行为
    • 编译器设计者必须回答三个主要问题

      • 1:编译器如何找出函数的定义?

        • 答案之一是包含template program text file,它就像一个header文件一样;另一种方法是要求一个文件命名规则
      • 2、编译器如何能够只实例化程序中用到member functions

        • 解决办法之一就是,根本忽略这项要求,把一个已经实例化的class的所有member function都产生出来;另一种策略是模拟链接操作,检查看看哪个函数真正需要,然后只为它(们)产生实例
      • 3、编译器如何组织member definition在多个.o文件中都被实例化呢

        • 解决办法之一就是产生多个实例,然后从链接器中提供支持,只留其中一个实例,其余忽略。另一个办法就是由使用者来导引“模拟链接阶段”的实例化策略,决定哪些实例才是所需要的

2、异常处理

  • Exception Handling快速检阅
  • 对Exception Handling的支持

3、执行期类型识别(Runtime Type Identification,RTTI)

  • Type-Safe Downcast(类型安全向下转换)
  • Type-safe Dynamic cast(类型安全动态转换)
  • Reference 并不是Pointers
  • Typeid运算符

4、弹性

  • 动态共享函数库
  • 共享内存

第六章:执行期语意学

1、对象的构造和析构

  • Destructor 必须被放在每个离开点之前

  • 全局对象

    • C++程序中所有的global objects都被放置在程序的data segment中,如果显示指定给他一个值,此object将以该值为初值,否则object所配置到的所有内存内容为0

    • munch策略:一个可移植但成本颇高的静态初始化(以及内存释放)方法

      • 1、为每一个需要静态初始化的文件产生一个_sti()函数,内含必要的constructor调用操作或inline expansions
      • 2、在每一个需要静态的内存释放操作(static deallocation)的文件中,产生一个_std()函数,内含必要的destructor调用操作,或是其inline expansions
      • 3、提供一组runtime library "munch"函数:一个_mina()函数(用以调用可执行文件中的所有_sti()函数),以及一个exit()函数(以类似方式调用所有的_std()函数)
    • 如何手机一个程序中各个object files的_sti()函数和_std()函数

      • 使用nm命令:nm命令会dump出object file的符号表格项目(symbol table entries)
  • 局部静态对象

    • local static class object 保证了什么样的语意

      const Matrix &
      identity(){
      static Matrix mat_identity;
      //....
      return mat_identity;
      }

      • mat_identity的constructor必须只能施行一次,虽然上述函数可能会被调用多次
      • mat_identity的destructor必须只能施行一次,虽然上述函数可能会被调用多次
  • 对象数组

    • 在cfont中,使用一个被命名为vec_new()的函数,产生出以class object构造而成的数组。

      • vec_new()的主要功能是把default constructor施行于class objects所组成的数组的每一个元素身上
    • 比较新近的编译器,包括Borland、Microsoft和sun,则是提供两个函数,一个用来处理“没有virtual base class”的class,另一个用来处理“内含virtual base class”的classes,后一个函数通常被称为vec_vnew()函数

  • default constructors和数组

2、new和delete运算符

  • int *pi = new int(5),由两步完成

    • 1、通过适当的new运算符函数实例,配置所需的内存

      // 调用函数库中的new运算符
      int *pi = __new(sizeof(int));

    • 2、将配置得来的 对象设立初值

      *pi = 5

      • 子主题 1
  • new 运算符实际上总是以标准的 C malloc()完成,delete运算符总是以标准的C free()完成

  • 针对数组的new语意

    • 在个别的数组元素构造过程中,如果发生exception,destructor就会被传递给vec_new(),只有已经构造妥当的元素才需要destructor的施行,因为它们的内存已经被配置出来了,vec_new()有责任在exception发生的时机吧那些内存释放掉

    • delete []p_array

      • 只有中括号出现,编译器才去寻找数组的维度,否则它便假设只有单独一个object被删除
    • 如果记录元素个数?

      • 为vec_new()所传回的每一个内存区块配置一个额外的word,然后把元素个数包藏在那个word中
  • placement operator new的语意

3、临时性对象

第五章:构造、析构、拷贝语意学

纯虚函数的存在

  • 可以定义和调用一个pure virtual 粉刺通:不过它只能被静态地调用,不能经由虚拟机制调用
  • 不要将virtual destructor 声明为pure

虚拟规格的存在

  • 一般而言,把所有的成员函数都声明为virtual function,然后再靠编译器的优化操作把非必要的virtual invocation(调用)去掉,并不是一个号的设计理念

虚拟规格中const的存在

  • 不把函数声明为const,意味着此函数不能够获得一个const reference或const pointer
  • 声明一个函数为const,然后发现其derived instance必须修改一个data member----头大了
  • 作者的意思就不用const了

重新考虑class的声明

1、“无继承”情况下的对象构造

  • 一个object的生命,是该object的一个执行期属性。

    • local object的生命从定义开始,该作用域结束
    • global object的什么和整个程序的生命周期相同
    • heap object的生命从被new运算符配置出来开始,到delete运算符摧毁为止
  • 抽象数据类型

  • 为继承做准备

2、继承体系下的对象构造

  • constructor可能内含大量的隐藏码,因为编译器会扩充每一个constructor,扩充程度视class T的继承体系而定。一般而言编译器所做的扩充操作大致如下:

    • 1、记录在member initialization list中的data members初始化操作会被方静constructor的函数本地,并以member的声明顺序为顺序

    • 2、如果有一个member并没有出现在member initialization list中,但它有default constructor,那么该default constructor必须被调用

    • 3、在那之前,如果class object有virtual table pointer(s),它(们)必须被设定初值,指向适当的virtual table(s)

    • 4、在那之前,所有上一层的base class constructors必须被调用,以base class的声明顺序为顺序(与member initialization list中的顺序没有关联)

      • 如果base class被列于member initialization list中那么任何显示指定的参数都应该传递过去
      • 如果base class没有被列于member initialization list中,而它有default constructor(或default memberwise copy constructor),那么就调用之
      • 如果base class是多重继承下的第二或后继的base class,那么this指针必须有所调整
    • 5、在那之前,所有virtual base class constructors必须被调用,从左到右,从最深到最浅

      • 如果class被列于member initialization list中,那么如果有任何显示指定的参数,都应该传递过去。若没有列于list之中,而class有一个default constructor,亦应该调用
      • 此外,class中每一个virtual base class subobject的偏移位置(offset)必须在执行期被存取
      • 如果class object是最底层(most-derived)的class,其constructors可能被调用;某些用以支持这一行为的机制必须被放进来
  • 虚拟继承

  • vptr语意学

    • 在一个class的constructor(或destructor)中,经由构造中的对象来调用一个virtual function ,其函数实例应该是在此class中有作用的那个。

    • constructor的调用顺序是:有根源而末端,由内而外

    • 什么决定一个class的virtual functions名单的关键?virtual table;virtual table如果被处理?通过vptr。所以为了控制一个class中有所作用的函数,编译系统只要简单地控制住vptr的初始化和设定操作即可

    • vptr何时被初始化

      • 在base class constructors调用操作之后,但是在程序员供应的代码或是“member initialization list中所列的members初始化操作”之前
    • constructor的执行算法通常如下

      • 1、在derived class constructor中,“所有virtual base classes”及“上一层 base class”的constructors会被调用
      • 2、上述完成后,对象的vptr(s)被初始化,指向相关的virtual table(s)
      • 3、如果有member initialization list的话,将在constructor体内扩展开。者必须在vptr被设定之后才做,以免有一个virtual member function被调用
      • 4、最后,执行程序员所提供的代码

3、对象赋值语意学

  • 只有在默认行为所导致的语意不安全或不正确时,才需要设计个copy assignment operator

  • 一个class对于默认的copy assignment operator,在以下情况,不会表现出bitwise copy语意

    • 1、当class内含一个member object,而其class有一个copy assignment operator时
    • 2、当一个class的base class有一个copy assignment operator时
    • 3、当一个class声明了任何virtual functions(一定不要拷贝有段class object的vptr地址,因为它可能是一个derived class object)时
    • 4、当class继承自一个virtual base class(不论此base class有没有UC偶朋友 operator)
  • 建议:尽可能不要允许一个virtual base class的拷贝操作

4、对象的效能

5、析构语意学

  • 如果class没有定义destructor,那么只有在class内含的member object(抑或class自己的base class)拥有destructor的情况下,编译期才会自动合成一个来。否则destructor被视为不需要,也就不需被合成(当然更不需要被调用)
  • 为决定class是否需要一个程序层面的destructor(或是constructor),请务必想清楚class object的生命在哪里结束或开始?需要什么操作保证对象的完整?

第四章:Function语意学

1、Member的各种调用方式

  • Nonstatic member function

    • C++的设计准则之一就是:nonstatic member function至少必须和一般的nonmember function有相同的效率

    • 编译器内部会将nonstatic member function转换为nonmember function,步骤如下:

      • 1、改写函数的signature(函数原型)以安插一个额外的参数到member function中,用以调用一个存取管道,使class object得以将此函数调用。该额外参数被称为this指针
      • 2、将每一个“对nonstatic data member的存取操作”改为经由this指针来存取
      • 3、将member function重新写成一个外部函数。将函数名称经过“mangling”处理,使它在程序中成为独一无二的命名
  • virtual member function

    • 举例

      若normalize()是一个virtual member function,其调用如下:
      ptr->normalize()

      将会被内部转换为:
      (* ptr -> vptr[1])(ptr);

      • vptr表示由编译器产生的指针,指向virtual table,它被安插在每一个“声明(或继承自)一个或多个virtual functions”的class object中,其实,其名称也会被mangled,因为在一个复杂的class派生体系中,可能存在多个vptrs
      • 示例中的1,代表virtual table slot的索引值,关联到normalize()函数
      • 示例中的第二个ptr表示this指针
  • static member function

    • 只有当一个或多个nonstatic data members在member function中被直接存取时,才需要class object

    • 静态成员函数的主要特性是它没有this指针

    • 依据主要特性有以下的次要特性

      • 1、它不能够直接存取其class中的nonstatic members
      • 2、他不能够被声明为const、volatile或virtual
      • 3、他不需要经由class object才被调用

2、virtual member function

  • 在C++中,多态(polymorphism)表示“以一个public base class的指针(或reference),寻址出一个derived class object”的意思

  • ptr的多态机能主要扮演一个输送机制(transport mechanism)的角色,经由它,可以在程序的任何地方采用一组public derived类型。这种多态形式被称为是消极的,可以在编译时期完成--virtual base class的情况除外

  • 识别一个class是否支持多态,唯一适当的方法就是看它是否有任何virtual function,只要class拥有一个virtual function,它就需要这份额外的执行期信息

    • 什么信息才能使在执行期调用正确

      • ptr所指对象的真实类型
      • 函数的地址,以便能够调用它
    • 实现上,在每一个多态的class object身上增加两个members

      • 1、一个字符串或数字,表示class的类型
      • 2、一个指针,指向某表格,表格中持有程序的virtual functions的执行期地址
    • 举例:ptr->z()

      • 一般而言,在每次调用z()时,并不知道ptr所指对象的真实类型,但知道经由ptr可以存取到该对象的virtual table

      • 虽并不知道哪个z()函数实例被调用,但知道每个z()函数地址被放在slot4中:(*ptr->vptr[4])(ptr);

        • vptr表示编译器所安插的指针,指向virtual table;4代表z()被指派的slot编号(关系到Point体系的virtual table)。
  • 多重继承下的virtual function

    • 多重继承下中支持virtual functions,其复杂度围绕在第二个及后继的base classes上,以及“必须在执行期调整this指针”这一点
    • 一般规则,经由指向“第二或后继的base class”的指针(或引用)来调用derived class virtual function
  • 虚拟继承下的virtual function

    • 不要在一个virtual base class中声明nonstatic data members

3、函数的效能

4、指向member function 的指针

  • 取一个nonstatic member function的地址,如果该函数式nonvirtual,得到的结果就是它在内存中的真实地址

  • 指向member function的指针的声明语法,以及指向“member selection就是.运算符”的指针,其作用是作为this指针的空间保留者

  • 支持“指向virtual member function”的指针

    • virtual function其地址在编译时期是未知的,所能知道的virtual function在其相关的virtual table中的索引值。即对一个virtual member function 取地址,得到的是一个索引值
  • 在多重继承下,指向member functions的指针

  • "指向member functions的指针"的效率

5、inline function

  • 一般处理inline函数有两个阶段

    • 1、分析函数定义,已决定函数的“intrinsic ability”(本质的inline能力),“intrinsic”(本质的,固有的),这里意指”与编译器相关“

      • 如果函数因其复杂度,或因其建构问题,被判断不可成为inline,它会被转为一个static函数,并在“被编译模块”颞产生对应的函数定义
    • 2、郑州的inline函数扩展操作是在调用的那一点上,这会带来参数的求值操作以及临时性对象的管理

  • 形式参数

    • 一般而言,面对“会带来副作用的实际参数”,通常都需要引入临时性对象,即如果实际参数是一个常量表达式,在替换之前先完成其求值操作;后继的inline替换,就可吧常量直接“绑”上去;如果既不是常量表达式,也不是带有副作用的表达式,那就直接替换
  • 局部变量

    • 一般而言,inline函数中的每一个局部变量都必须被放在函数调用的一个封闭区段中,拥有一个独一无二的名称
posted @ 2022-02-08 18:58  牛犁heart  阅读(257)  评论(0编辑  收藏  举报