关于vptr和vtbl
父类有虚函数,子类也必定有,并且含有自己的虚指针(vptr)。虚指针指向虚表(vtbl),虚表中存放函数指针,指向虚函数所在位置。父类和子类的同名成员函数完全无关联,是不同的函数。子类override的父类虚函数,拥有与父类不同的函数指针。
静态绑定与动态绑定
c语言中的函数调用(call)是静态绑定(static binding),调用函数时总是由编译器解析成固定地址然后跳转,调用结束再跳转回初始的地址。
c++中通过指针调用虚函数是动态绑定,访问虚指针指向的虚表中含有的多个函数地址。
让编译器理解成动态绑定(*p->vptr[n]) (p);得符合三个条件:
1.通过指针调用
2.指针是upcast向上转型 *A pa = new B A是B的父类
3.调用的是虚函数
虚函数的两种用法:
1.template method 通过子类对象调用父类函数,将子类对象的地址(this pointer)作为参数传给父类函数(所有类的成员函数都有隐含的this pointer作为参数),在执行过程中遇到子类中特有的成员函数时跳转到子类,调用结束后又返回继续执行父类函数,正是利用了动态绑定的特性
2.多态 声明容器中的指针时必须指向父类,在使用时可以让它指向子类
关于const
不能由const对象调用non-const函数(不保证data members不变):
const String str(“hello world”);
str.print();
虽然const版本函数也可以被non-const object调用,但当成员函数的const和non-const版本同时存在,const object只能调用const版本,non-const object只能调用non-const版本
array new
array new时,数组的内存除了(元素个数*每个元素的大小)外,还有一个大小为4字节的数用来记录数组元素个数,使数组作为一个整体被分配内存,并且使编译器快速得知构造以及析构函数需要被调用的次数。
绕过自己重载的构造以及析构函数,强制使用默认的全局函数的方法:
Foo* p = ::new Foo(7);
::delete p;
Foo* pArray = ::new Foo[5];
::delete []pArray;
重载placement new:
可以为operator new()写出多个版本,但每个版本的声明必须有独特的参数列(或者类型不同,或者参数个数不同),其中第一参数必须是size_t,其余参数以new()括号中指定的placement arguments为初值。
也可以重载class member operator delete()并写出多个版本(参数列表与operator new对应,即使并未一一对应,也不会报错,意味着放弃处理ctor发出的异常),但只有当new所调用的ctor抛出exception,才会调用重载版的operator delete()。它主要用来归还未完全创建成功的对象所占的内存。