五条基本规则:
1、如果基类已经插入了vptr, 则派生类将继承和重用该vptr。vptr(一般在对象内存模型的顶部)必须随着对象类型的变化而不断地改变它的指向,以保证其值和当前对象的实际类型是一致的。
2、在遇到通过基类指针或引用调用虚函数的语句时,首先根据指针或引用的静态类型来判断所调函数是否属于该class或者它的某个public 基类,如果
属于再进行调用语句的改写:
1
|
(*(p->_vptr[slotNum]))(p, arg-list);
|
其中p是基类指针,vptr是p指向的对象的隐含指针,而slotNum 就是调用的虚函数指针在vtable 的编号,这个数组元素的索引号在编译时就确定下来,
并且不会随着派生层的增加而改变。如果不属于,则直接调用指针或引用的静态类型对应的函数,如果此函数不存在,则编译出错。
3、C++标准规定对对象取地址将始终为对应类型的首地址,这样的话如果试图取基类类型的地址,将取到的则是基类部分的首地址。我们常用的编译器,如vc++、g++等都是用的尾部追加成员的方式实现的继承(前置基类的实现方式),在最好的情况下可以做到指针不偏移;另一些编译器(比如适用于某些嵌入式设备的编译器)是采用后置基类的实现方式,取基类指针一定是偏移的。
4、delete[] 的实现包含指针的算术运算,并且需要依次调用每个指针指向的元素的析构函数,然后释放整个数组元素的内存。
5、 在类继承机制中,构造函数和析构函数具有一种特别机制叫 “层链式调用通知” 《 C++编程思想 》
C++标准规定:基类的析构函数必须声明为virtual, 如果你不声明,那么"层链式调用通知"这样的机制是没法构建起来.从而就导致了基类的析构函数被调用了,而派生类的析构函数没有调用这个问题发生.
如下面的例子: