虚函数,抽象基类

编译器处理虚函数的方法:

给每个对象添加一个隐藏成员,隐藏成员保存了一个指向函数地址的数组指针,数组被称为虚函数表,虚函数表存储了为类对象声明的虚函数的地址,比如基类包含一个指针,该指针指向基类中所有的虚函数的地址查找表,派生类将继承这个虚函数地址表,如果重新定义了虚函数,那么派生类的虚函数表中相应的函数地址会被替换,调用虚方法的时候,函数调用的就是这个虚地址里的函数,由于被替换,所以可以用基类指针调用派生类方法,调用虚函数时,程序将查看虚函数表中的地址,并转向相应的函数地址表,

总之使用虚函数的时候,在内存和执行速度上都有一定的成本,包括:

每个对象都将增大,增大量为存储虚函数地址的数组的空间;

对于每个类,编译器都要创建一个虚函数地址表;

对于每个函数调用,都需要执行一项额外操作,即到表中查找地址;

虽然非虚函数比虚函数效率高,但不具备动态联编功能;

析构函数必须是虚析构函数:这是由于基类指针指向派生类对象造成的;

虚函数与抽象基类是不一样的;

友元不能是虚函数,只有成员函数才能是虚函数,

如果定义的类被用作基类,则应该将那些在派生类中重新定义的方法声明为虚的,析构函数是一定会被重载的,因此析构函数必须是虚函数;构造函数不能是虚函数,调用顺序是先调用基类构造函数,在调用派生类构造函数,如果是虚方法,则将调用派生类的构造函数,然后调用基类的构造函数,这与继承的机制相反;

如果派生类中重新定义的函数与基类虚函数的函数特征标不同即重载,那么将隐藏基类虚函数,根据返回类型协变,如果只是修改返回值,将不会隐藏基类虚函数,

如果确实有必要重载基类虚函数(函数特征标不同),那么应该在派生类中重载所有的基类版本,如果只重载一个版本,那么另外的将被隐藏,

 如上,Hovel必须重写所有的基类虚函数,这也不能通过作用域解析运算符来调用,

总结虚函数注意:

当定义为虚函数时,通过指针或引用调用时,调用的函数为指向对象的类型中的函数,而不是指针的类型下的函数;

虚函数表:虚函数的调用方式:指针在指针指向的对象的虚函数数组内找到要调用的函数指针地址,然后调用,所以虚函数可以调用指针指向类型的函数;由于虚函数表的存在,他只与对象有关,

隐藏基类方法:如果子类对虚函数进行重载,将隐藏基类的虚函数,因此如果重载,则必须重新定义所有的同名基类虚函数;

总的来说,虚函数是对于单继承模式下函数多态的一种拓展,当出现菱形继承时情况还会复杂一些;

抽象基类:

is-a关系是数学中的包含关系,一个集合属于另一个集合,但是当两个集合出现相交时就有问题了,比如圆是特殊的椭圆,但椭圆类有两个焦点,圆只有一个圆心,即属性成员出现了冲突,除此之外圆和椭圆可以发生的动作也是不同的,是不能通过函数重载来解决的, 单独定义两个类似乎更简单一些;

抽象基类说明:

但是圆和椭圆是由共同点的,也就是说这种相交关系更像是同辈,而不是父子,因此可以给他们两个指定一个基类,包含他们的交集,有时这种类不需要被实例化,此时我们称这种类为抽象基类,比如圆和椭圆的抽象基类,按理来说他是一个图形,但它只包括了中心点的坐标,他没有半径,长短焦距这些参数,那么他其实无法实例化为一个图形,这时它理论上就无法被实例化,同时实际上他也没有必要被实例化,那么他就成为一个抽象基类就好了,这点我们可以这么来举例:人和狮子是哺乳动物的派生类,但哺乳动物无法被实例化,他有几个脚?几个眼睛?什么样的运动方式?都没有,怎么运动?如果没有运动,只是声明的话是没有内存空间的,程序就没法访问它,对于没有实际意义的动作,就把他们声明为纯虚函数,对于没有意义的属性,直接不声明它,那么哺乳动物就是一个它通过加入纯虚函数来实现,

纯虚函数的结尾处为 =0;如下图中的

virtual double Area()const =0;

纯虚函数也可以有定义,但他不能同时是内联的,即不能再类声明中定义;

概念称呼:哺乳动物是抽象类,人和狮子就是具体类,即可以创建对象的类,

成为具体类的方法:要想成为具体类就必须重写所有的基类的纯虚函数;

所有的当类与动态内存分配结合起来时,情况就会很微妙;如果再加上继承,

动态内存分配涉及到复制构造函数,析构函数,重载赋值运算符,当出现需要动态内存分配的初始化的指针成员时,必须重载这三个指令集,

否则会出现内存泄漏(析构函数)和浅拷贝(赋值运算符,复制构造函数)以及重复清理堆空间(析构函数),

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值