C++学习之virtual虚函数

1、虚函数的基本概念

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

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

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

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

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

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

class derive1:public base{
    public:
          void who(){
              std::cout<<"this is the class of derive1"<<std::endl;
          }
    	  virtual ~derive1(){
              std::cout<<"the class of derive1 is deleted"<<std::endl;
          }
};

class derive2:public base{
    public:
    	  void who(){
              std::cout<<"this is the class of derive2"<<std::endl;
          }
    	  virtual ~derive2(){
              std::cout<<"the class of derive2 is deleted"<<std::endl;
          }
};

int mian(){
    base obj,*ptr;
    derive1 obj1;
    derive2 obj2;
    //---------------------------//
    ptr = &obj;
    ptr->who();
    ptr = &obj1;   //通过基类指针调用,此时才是动态联编
    ptr->who();
    ptr = &obj2;
    ptr->who();
    //----------------------------//
    obj.who();
    obj1.who();   //通过“对象.成员函数”的形式调用,虽然达到了一样的效果,但在程序运行时已经是静态调用了。
    obj2.who();
}

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

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

虚析构函数:
析构函数的虚函数比较特别,它的函数名可以是不同的,解释这个原因,我们要想想为什么虚析构函数要出现。我们想,前面提到虚函数的动态联编要实现必须用基类的指针调用来实现,那现在假如我要用delete收回这个对象指针,可是无论其到底是基类指针还是派生类此时都是基类的指针,那系统怎么判别用谁的析构函数呢?此时就用到了析构函数的虚函数,析构函数的虚函数是一类函数,凡是派生类的析构函数都是基类虚函数的一个具体展现。这样编译器就能根据对象调用合适的析构函数了。

2、vptr与vtable (虚函数指针与虚函数表)

未完待续。。。。。。。。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值