C++编程思想杂记(15章 多态性和虚函数)

封装使接口从具体实现中分离开来,而虚函数则根据类型来处理解耦。继承可以使多个类型使用相同的代码,而虚函数可以表现一个类型与其另一个相似类型的区别。


函数体和函数调用相联系称为捆绑,即binding,运行前捆绑即编译器可以确定对象的类型,运行时捆绑即为多态,在运行时确定对象的类型调用相应函数,这样可以实现接口不变而实现变化,使程序的扩展性提高


根据C++对象模型,会对有虚函数的类创建一个vtbl virtual table。通过vptr来指向。

对于一个没有虚函数的类,其大小就是数据成员的大小,如果有虚函数,就多加一个指针的大小

class A
{
    int b;
public:
    virtual int function()
    {
        return 1;
    }
};
class B
{
    int b;
public:
     int function()
    {
        return 1;
    }
};

B大小为4,A为8


如果把成员b注释掉,B大小为1,A为4,会插入一个成员来使类对象大小不为0,如果有虚函数,就会取代这个特殊成员。


vptr指向虚函数表的起始地址,所有派生类有相同的虚函数表顺序(自己未重新定义新的虚函数)


虚函数在汇编中的执行情况,假设vptr在对象的开头


调用为i.adjust(1);则

push 1

push si

mov  bx,word ptr [si]

call   word ptr [bx+4]


这里,首先将函数调用的参数压入栈,参数进栈顺序和C一样,从右向左,注意压入si的是this指针,每次成员函数调用,其实都会以this作为参数(static 成员函数除外因为它不属于任何一个对象)

然后取si的指向,即this的起始的指向内容到bx,这也是vptr指向的地址即vtbl。然后根据事先设定的虚函数,假设有三个虚函数创建顺序分别是play,what,adjust,则这个指针需要移动两个函数指针来指向adjust,如果一个指针是2字节,则这里就可以计算到adjust虚函数的地址然后调用。


在构造函数内部会自动初始化vptr以保证这个机制的实现。

这个机制其实没有那么高效,因为它必须有一张vtbl,需要运行时寻址

抽象基类和纯虚函数,抽象基类就类似java的interface,这使得在vtbl中会给该函数保留一个位置,这使得该类的vtbl并不完整。这使得无法创建出该类的对象。


一个类全是纯虚函数,该类就可以称为纯抽象类,pure abstract class。


在基类中,可以给纯虚函数提供定义。但是还是不能产生该抽象基类的对象。

对象切除,就是虚函数调用的时候不是通过指针,而是通过对象,这样在压栈的时候,就会发生对象切除,将派生类对象切成基类对象,这和指针的向上类型转换完全不同。

纯虚函数可以避免这一点,因为基类对象无法生成


虚函数重载,和普通成员函数一样,如果基类有两个同名虚函数,而派生类对其中一个进行了重写,则另一个会被屏蔽。如果我们修改了虚函数的参数列表,则当我们用基类指针引用派生类对象,调用虚函数时,只会调用基类版本,因为基类没有对应新参数列表的虚函数。


注意:虚函数重写时,不能修改返回类型

编译器会在构造函数的起始插入一段vptr初始化代码。如果是派生类的构造函数,则vptr的状态,由最后调用的构造函数决定。每次构造函数调用都会使这个vptr指向该构造函数类型的vtbl


为什么在构造函数中调用虚函数是不工作的。因为构造函数中。对象没有创建完全,而虚函数可能使用到派生类未初始化的对象。

其次构造函数调用中,之前提到是不断修改vtpr的,那么就无法正确调用对应虚函数


虚析构函数,意义在于,delete一个指向派生类对象的基类指针时,可以成功调用派生类的析构函数再调用基类的析构函数,如果不是虚析构函数,就只会调用基类的析构函数,造成对象未删除完全。


纯虚析构函数,其他纯虚函数不同,如果其他纯虚函数在派生类中不定义,派生类也是个抽象类,不能产生该类型对象,而纯虚析构函数的基类,其派生类不是抽象类


无论是构造函数还是析构函数,里面调用虚函数,都无法使用虚函数机制,只会调用同名本地函数。这两者的区别在于,构造函数是因为vtpr还不确定,析构函数是在调用基类的虚函数时,派生类对象已被摧毁,无法调用派生类虚函数。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值