本篇笔记主要分为两个主要部分,第一部分关于对象模型,第二部分是关于new和delete的更加深入的学习。
-
对象模型
-
关于vptr(虚指针)和vtbl(虚函数表)
只要用到了虚函数,对象中就会多一个指向虚函数表的虚指针。在32位环境下,将占4Bytes的空间。
在vtbl中,每一项都是指向自己类应当调用的虚函数的函数指针。
2.静态绑定与动态绑定
在C中,对于不同的函数名采用静态绑定的方法,每个函数直接对应了一个地址,存储在相应的位置中。在C++中,非虚的成员函数也用静态绑定的方式被存储。如上图中的A::func1()等成员函数。
不过对于虚函数,C++中采用了动态绑定的方法。在上图中,每个虚函数都存储在虚函数表中。当调用虚函数时,编译器会随着上图中的路径找到正确的函数调用。
由于动态绑定,不管什么地方调用虚函数,总能得到正确的结果这个机制限制了虚函数应该被虚函数覆盖。
虚函数可以由以下的两种方式得到。
一个非常常用的基于虚函数的方式是建立一个指向抽象类的指针链表,这是多态的体现。
关于类的对象模型的内存分配,涉及到对象的位对齐的规则,在博客中的另一篇文章中有简单介绍。
-
再谈new和delete
-
定位new和delete运算符及其重载
new和delete在使用时可以有一个可选的指针类型的参数,用来指定内存分配的起始地址。如果没有这一参数,则会在堆空间中自动分配一段合适大小的空间。
默认的一个定位new函数是:
void* operator new(size_t size,void *start)
在使用时可以采用例如如下的方法:int *p = new(0x12345678) int;
事实上,我们还可以使用别的参数列进行new操作,我们也可以对operator new和operator delete进行重载。例如下面的两个常用的参数列:
void* operator new(size_t size,long extra)//extra参数用于多申请一段存储空间,专门用来存储一些特别的信息,例如引用计数的信息。
void* operator new(size_t size,long extra,char init)
其固有的定义是:
inline void * operator new(size_t size){return malloc(size);}
inline void * operator new[](size_t size){return malloc(size);}//这里在调用时size会自动进行计算。
inline void operator delete(void * ptr,size_t size){free(ptr);}
inline void operator delete[](void * ptr,size_t size){free(ptr);}
注意,如果需要进行重载,第一个参数始终应该是size_t格式的。此外,通常情况下自己定义的delete不会被调用,而是继续采用默认的方法,直接释放内存。只有当new操作失败是,才会去寻找具有相同输入参数列的operator delete函数;如果没有定义相应的delete函数,也可以编译通过,就代表放弃处理new失败的这一异常。