虚函数

在继承中,如果基类和派生类中定义了同名的成员函数,当用基类指针指向公有派生类的对象后,可以用虚函数来实现通过基类指针找到相应的派生类成员函数。

 

一. 为什么引入虚函数

     根据肤质兼容规则,任何被说明为指向基类对象的指针都可以指向它的公有派生类对象,若试图指向它的私有派生类对象是被禁止的。反之,不能将一个声明为指向派生类对象的指针指向其基类对象。

     声明为指向基类对象的指针,当它指向公有派生类对象时,只能利用它来直接访问派生类中从基类继承来的成员,不能直接访问共有派生类中其他成员。若想访问其公有派生类的其他成员,可以将基类指针显示类型转换为派生类指针来实现。

 

    例:

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

class base
{
 public:
   void who()
   {cout<<"this is the class of base!"<<endl;}
};


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

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

void main()
{
base obj1,*p;
derive1 obj2;
derive2 obj3;
p=&obj1;
p->who(); //base
p=&obj2;
p->who(); //base
((derive1*)p)->who(); //derive1
p=&obj3;
p->who(); //base
((derive2*)p)->who(); //derive2
obj2.who(); //derive1
obj3.who(); //derive2
}

运转结果:

  this is class of base!

  this is class of base!

  this is class of derive1!

  this is class of base!

  this is class of derive2!

  this is class of derive1!

  this is class of derive2!

 

当指针指向不同对象时执行不同的操作,需要实现此功能,需引入虚函数。

 

二. 虚函数的定义与使用

      1.定义

       在基类中的某个成员函数被声明为虚函数后,此成员函数就可以在一个或多个派生类中被重新定义。在派生类中重新定义成员函数时,都必须与基类的原型相同。

       虚函数是一种非静态的的成员函数:

         virtual <类型><函数名><参数表>

注意:

       (1)派生类中的重新定义基类的虚函数,可以不加virtual,因为虚特性可以传递,但函数原型必须与基类中的完全相同,否则丢失虚特性。

       (2)基类说明的虚函数有下传给派生类的特质。

       (3)析构函数可以是虚函数,但构造函数不能是虚函数。若某类定义有虚函数,则其析构函数应当是虚函数。

       (4)在类体系中访问一个虚函数时,应当使用指向基类的指针或对基类的引用,以满足多态性的要求。

       (5)在派生类中重新定义虚函数时,必须保证派生类中该函数的返回值和参数与基类中的说明完全一致,否则属于重载(参数不同)或一个错误(参数相同,返回值不同)

       (6)若在派生类中没有重新定义虚函数,则派生类的对象将使用其基类中的虚函数代码。

       (7)虚函数必须是类的一个成员函数,不能是友元,但可以是另一个类的友元。虚函数不能使一个静态成员函数

       (8)构造函数只能用inline修饰,不能是虚函数。

       (9)一个类的虚函数仅对派生类中重新定义的函数起作用,对其他函数没有影响。在基类中使用虚函数保证了通过指向基类对象的指针调用基类的一个虚函数时,系统对该调用进行动态绑定,而使用普通函数是静态绑定。

       (10)使用虚函数后,不得使用类作用域符区分符强制指明要引用的虚函数。

       (11)若派生类中没有再定义基类中已有的虚函数,则指向该类对象的指针或引用名引用虚函数总是引用距离其最近的一个基类中的虚函数。

       (12)若在基类的构造(析构)函数中也引用虚函数,则所引用的也只能是本类的虚函数,因此此时派生类中的构造(析构)函数的执行尚未完成。

 

       2.虚函数与重载函数的关系

          (1)重载函数可以是成员函数或友元函数,而虚函数只能是成员函数。

          (2)重载函数的调用是以所传递参数序列的差别作为调用不同函数的依据,虚函数是根据对象的不同去调用不同类的虚函数。

          (3)虚函数在运行时表现出多态性,重载函数在编译时出现多态性。 

      例:虚函数与重载函数的关系

#include<iostream>
class base
{
public:
  virtual void f1()
  {cout<<"f1 function of base"<<endl;}
  virtual void f2()
  {cout<<"f2 function of base"<<endl;}

  virtual void f3()
  {cout<<"f3 function of base"<<endl;}
  virtual void f4()
  {cout<<"f4 function of base"<<endl;}

};

class derive:public base
{
  void f1()
  {cout<<"f1 function of derive"<<endl;}
  void f2(int x)   //丢失虚特性,变为一般重载函数
  {cout<<"f2 function of base"<<endl;}

  //int f3()   错误,返回值类型不同
  //{cout<<"f3 function of base"<<endl;}
  void f4()
  {cout<<"f4 function of derive"<<endl;}

};

void main()
{
  base obj1,*ptr;
  derive obj2;
  ptr=&obj1;
  ptr->f1();
  ptr->f2();
  ptr->f3();
  ptr=&obj2;
  ptr->f1();
  ptr->f2();
  ptr->f4();

}


Result:

f1 function of base

f2 function of base

f3 function of base

f1 function of derive!

f2 function of base

f3 function of base


3. 在构造函数和析构函数中调用虚函数

    编译系统对构造函数和析构函数中调用虚函数采用静态联编,即它们所调用的虚函数是自己的类或基类中定义的函数而不是在任何派生类中重定义的函数。同样,使用对象调用虚函数也采用静态联编。

    例:在构造函数中调用虚函数

      

#include<iostream.h>
class base
{
public:
  base(){}
  virtual void vf()
    {cout<<"base::vf() called"<<endl;}
};

class son:public base
{
 public:
   son(){vf();}
   void g(){vf();}
};

class grandson:public son
{
 public:
  grandson(){}
  void vf()
    {cout<<"grandson::vf() called\n";}
};
void main()
{
  grandson gs;
  gs.g();
}
Result:

base::vf() called

grandson::vf() called


4. 空的虚函数

    派生类不一定必须实现基类中的虚函数,如果派生类想通过虚函数机制访问虚函数,则必须建立一条从基类到派生类的虚函数路径。许多没有使用虚函数的中间类也必须声明该函数,以保证其后的派生类能使用该虚函数,可以通过声明空函数来达到此目的。


三. 纯虚函数和抽象类

1. 纯虚函数的概念

      如果基类只表达一些抽象的概念,而并不与具体的对象相联系,但它又必须为它的派生类提供一个共有的界面,这种情况下,可以将基类中的虚函数定义成纯虚函数。纯虚函数是一种没有具体实现的特殊的虚函数。一个基类中有一个纯虚函数时,则在它的派生类中至少有一个虚函数。

  纯虚函数定义格式:

      virtual <类型>(函数名)(参数表)=0

由于纯虚函数所在的类中没有它的定义,在类的构造函数和析构函数中不允许调用纯虚函数,否则会导致错误,但其他成员函数可以调用纯虚函数。


2.抽象类的概念

   抽象类:一个类至少含有一个纯虚函数。

  具体类:不含纯虚函数。


注:


四. 虚析构函数

      虚析构函数的说明方法:在虚析构函数前面加上关键字virtual,则析构函数被说明为虚析构函数。

      如果一个基类的析构函数被说明为虚函数,则它的派生类的析构函数无论是否使用virtual说明,都自动成为虚析构函数。

      当指向基类的指针指向新建立的派生类对象而且基类和派生类都调用new想堆申请空间时,必须将基类的析构函数声明为虚函数,这样才能在程序结束是自动调用他,从而将派生类对象申请的空间退还给堆。

      由于派生类的析构函数不可能和抽象类的析构函数同名,因此提供一个默认的析构函数是必要的。一般情况下,抽象类的析构函数是在释放派生类对象时由派生类的析构函数隐含调用的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值