【C++学习笔记】虚函数(一)

12.3 昨天居然断更了,唉,写博客真是很需要毅力呀,更新上今天的学习笔记。

上次我们讲到多态性的定义以及简述了实现方式以及静态编译和动态编译的概念。这次,我们来具体讲一讲虚函数。

什么是虚函数

在某基类中声明为 virtual 并在一个或多个派生类中被重新定义的成员函数,
用法格式为:virtual 函数返回类型 函数名(参数表) {函数体};实现多态性,通过指向派生类的基类指针或引用,访问派生类中同名覆盖成员函数。

解释:对概念做进一步解释,这个定义中对虚函数有两个层面,第一层是在基类中用virtual关键字声明的,第二层是要在多个派生类中被重新定义。

所以,我们如果用:“用virtual定义的函数就是虚函数”来定义虚函数是不准确的。
普通的函数是在编译时就已经确定好其功能并且不能改变了,而虚函数允许函数调用与函数体之间的联系在运行时才建立,也就是在运行时才决定如何动作,就是动态联编。

我们做一个简单的类比就是普通的函数是一个工具,它在使用前就已经确定了这个工具到底是锤子还是榔头还是铁锹,而函数重载则是
把工具集成化做成一把瑞士军刀,你想用这个工具做什么,就能抽出相应的工具,函数重载开起来似乎很方便了,但是这把瑞士军刀里到底有什么工具是定死了的,也就是说,**在编译后,这把军刀里有哪些工具是无法改变的。**而虚函数是什么呢?虚函数就是给你钱然后告诉你一个工具商店,我这个程序要使用一个工具,具体是什么工具呢,我不知道,反正遇到了要调用这个工具的地方拿钱自己去商店买相应的工具就好了。这样虽然每次使用工具都要专门跑到商店里去买,效率低了,但保证了面对不同的问题我都有法子解决。

总之,虚函数是动态联编的基础。虚函数是成员函数,而且是非static的成员函数。

##12.11整整一星期没写了,太懒了自己

先放个栗子(标准用法)来说明这个问题吧:

#include<iostream.h>
class base
{public:
	virtual void who()
	{cout<<"this is the class of base!"<<endl;}
	virtual ~base{cout<<" the class of base is deleted"<<endl;}
	};
	

class derive1: public base
{public:
	void who()
	{cout<<"this is the class of derive1!"<<endl;}
	~derive1{cout<<" the class of derive1 is deleted"<<endl;}
	};
class derive2:public base
{public:
	void who()							//这里who()是一个虚函数,virtual可加
	{cout<<"this is the class of derive2!"<<endl;}	//可不加。(注意点1)
	~derive2{cout<<" the class of derive2 is deleted"<<endl;}
	};
	
 int main{
 base obj,*ptr;
 derive1 obj1;
 derive2 obj2;
//----------------------------------------------------------------------------
 ptr = &obj;
 ptr->who();
 ptr = &obj1;				//通过基类指针调用,此时才是动态联编
 ptr->who();
 ptr = &obj2;
 ptr->who();
//----------------------------------------------------------------------------
obj.who();
obj1.who();				//通过“对象.成员函数”的形式调用,虽然达到了一样的效果,
obj.who();					//但在程序运行时已经是静态调用了。(注意点3)
 }

虚函数的一些性质和注意点

  1. 通过定义虚函数来使用C++提供的多态机制时,派生类应该从它的基类公有派生。赋值兼容规则成立的前提条件是派生类从其基类公有派生。(你要先规定工具是大家公用的,才能说明虚函数)
  2. 必须首先在基类中定义虚函数。在实际应用中,应该在类等级内需要具有动态多态性的几个层次中的最高层类内首先声明虚函数。
  3. 在派生类对基类中声明的虚函数重新定义时,**关键字virtual可写可不写。**但在基类中的virtual关键字是必须要加的。
  4. 使用对象名和点运算符的方式可以调用虚函数,但这种调用在编译时进行的是静态联编,并没有充分运用静态函数的特性。只有通过基类指针访问虚函数时才能获得运行时的多态。(这个点相当重要,且不好理解,大家就直接记住吧,只有通过基类指针访问的虚函数使用了动态联编)
  5. 一个虚函数无论被公有继承多少次,它仍然保持其虚函数的特性。(该是虚的就是虚的,一旦基类定义了,后面怎么做都改变不了它的性质。)
  6. 虚函数必须是其所在类的成员函数,而不能是友元函数也不能是静态成员函数。但是,虚函数可以在另一个类中被声明为友元函数。(因为系统是依据特定的对象来决定该激活哪个函数,所以要保证类与虚函数结构的一一对应,说白了,就是一个类要保证对应一个虚函数,这样系统才知道该用哪个结构。)
  7. **内联函数不能是虚函数。**这个点很有意思,有人可能会问,在栗子中函数不是定义在类内吗?怎么内联函数就不能变成虚函数了呢?这是因为严格来说,内联函数和外联函数不是通过是否在类内定义来区别的。在这里,虽然虚函数是在类内定义的,但编译时编译器仍然把这个函数看作是非内联的(气不气)。原因是内联函数不能再运行中动态确定其位置的,系统给了你在类内定义的形式,但实际上一但定义了是虚函数,它就不可能是内联函数。
  8. **构造函数不能是虚函数,但析构函数可以。**因为虚函数作为运行过程中的多态基础,是以对象为核心的,而构造函数是用来构建对象的,也就是说先有对象,后有虚函数,所以构造函数绝对不可能是虚函数。但析构函数是用来销毁对象的,是在对象后,所以可以是虚函数。

虚析构函数

析构函数的虚函数比较特别,它的函数名可以是不同的,解释这个原因,我们要想想为什么虚析构函数要出现。

我们想,前面提到虚函数的动态联编要实现必须用基类的指针调用来实现,那现在假如我要用delete收回这个对象指针,可是无论其到底是基类指针还是派生类此时都是基类的指针,那系统怎么判别用谁的析构函数呢?

此时就用到了析构函数的虚函数,析构函数的虚函数是一类函数,凡是派生类的析构函数都是基类虚函数的一个具体展现。这样编译器就能根据对象调用合适的析构函数了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值