极客班C++面向对象高级编程(下)第二周笔记


关于vptr(虚函数表指针)和btbl(虚函数表)

极客班C++面向对象高级编程(下)第二周笔记_第1张图片

最右边的是A、B、C三个class的继承关系,左边的是A,B,C三个类的三个对象a,b,c的对象模型(表示在内存中的结构);

在对象模型中,会有虚函数表指针和数据,虚函数表指针是内存头,即对象的地址也是虚函数表的地址;

虚函数表指针vptr是指向虚函数表vtbl的,vtbl内会有类里面所有虚函数的地址,并且是按在类里面声明的顺序,按数组方式排列(表头是0);

如果有一个指针p指向对象c,那么我们就可以通过这个指针和地址,直接调用对象内的虚函数(而不用通过函数名),上面的便有它的调用方法(通过指针p,找到虚表指针vptr,再找出它的虚表vtbl取出其中的第n个,把它当函数指针,去调用p,因为p是调用者所以this就是指向p)

当一个函数调用,同时满足下面的3个条件时,(1)对象通过指针被调用的,(2)对象是向上转型的(它定义的是自己的类型,但其实是父类的类型,这就叫做向上转型),(3)调用的是对象的虚函数,编译器就会把这个调用动作编译为上面所说的调用方式,称为动态绑定(相对的是,不通过指针调用的静态绑定,)。

关于this

极客班C++面向对象高级编程(下)第二周笔记_第2张图片

在调用流程图中,子类对象调用了父类的函数,调用者是子类对象,所以this是指向类子对象;

这样,被调用的函数里面的每一个函数都会写成通过this指针调用的形式,上面便是;

接着如果被调用的函数是虚函数,就会被写成动态绑定,上面便是;

这就是之前所说的,满足三个条件的函数调用,会被编译器写成动态绑定形式。

关于Dynamic Binding

静态绑定,在编译成汇编时,会有直接的地址给出;

而通过指针的动态绑定,则没有。

关于const

成员函数的名称后面,可以加上const,表示它不打算改变class的data。

之前有提及成员函数不加const的,可能使数据发生改变;也提及过加与不加const的成员函数的签名是不一样的;解释如下:

极客班C++面向对象高级编程(下)第二周笔记_第3张图片

const object是不能调用non-const

member functions的,前者说不能改变,但后者则可能改变,后者不能包容前者。

而且,当成员函数的const和non-const版本同时存在,const object只能调用const版本,non-const object也只会调用non-const版本。

如果当初设计String中的print()时未指明const,那么上行便是经由const object调用non-const member function,这不符合上面所以的规则显然是会出错的。

(对于加不加const,其实在函数起名的时候就已经可以确定了,因为名称代表着它的动作,从动作就可以知道是不是要加const)

关于new和delete

如何重载new和delete?

new和delete是表达式,表达式的行为是不能变的;

之前我们学过,new和delete是会被分解多个步骤的;

据此,我们可以通过重载它分解步骤里所调用的函数,实现对new和delete的重载。

极客班C++面向对象高级编程(下)第二周笔记_第4张图片

上面便是重载的接口。

operator new( new [ ] )和operator delete( delete [ ] )的重载

operator new和operator delete的实现:

极客班C++面向对象高级编程(下)第二周笔记_第5张图片

operator new[]和operator delete[]的实现:

极客班C++面向对象高级编程(下)第二周笔记_第6张图片

为什么要加4?下面有解释。

调用示例

极客班C++面向对象高级编程(下)第二周笔记_第7张图片

在示例中上面的是无虚函数时的情况,下面的是有虚函数时的情况。

极客班C++面向对象高级编程(下)第二周笔记_第8张图片

我们已经把重载写好,重载里面还会用cout打印出调用数据。

调用测试,分别用1,2,3,4行进行;

第1,2行的调用数据显示,有虚函数的比无虚函数的多4,因为多了虚函数表指针(在32位环境下,指针占4);

第3,4行,动态分配array时,会顺序调用5次构造函数,array

delete则是逆顺序调用析构函数;它在数据包之前会有一块大小为4的内存块,作为计数器counter,记录数据的个数(这里counter的是5,表示有5个元素,这能使编译器很快知道要调用5次构造和析构函数),所以它的大小是每个object的大小之和,加上4(注,每个object都会有自己的虚函数表指针,不要理解错了)

极客班C++面向对象高级编程(下)第二周笔记_第9张图片

如果调用时采用下行的方式,那么将不会进入刚才重载的函数,而是调用原有的全局函数。

极客班C++面向对象高级编程(下)第二周笔记_第10张图片

关于class member operator new()的更多重载:

之前我们已经重载成员函数的operator new()(成员函数的调用者一定是这种class的对象),如果我们要重载更多的版本,那么要在它原来的基础上增加参数列(原来的只有一个size_t类型的形参),并且它的第一个参数必须是size_t类型(代表某块内存的大小的类型,size type),每个版本的重载也必须有不同的参数列,上面便是声明和调用形式,调用时要在new()的小括号里写上除了第一参数之外的其余参数,这些小括号里额外的参数叫做placement

new。

我们也可以重载与上面有额外参数的相对应的class member operator

delete(),但它并不能被delete语句调用,它只有在当对应的operator new()所调用的ctor抛出异常时,才会被调用,去delete未完成创建的内存块。

你可能感兴趣的:(极客班C++面向对象高级编程(下)第二周笔记)