多态性和虚函数

  • 多态性:是指具有不同功能的函数可以用同一个函数名,这样就可以用一个函数名调用不同内容的函数。

向不同的对象发送同一个消息,不同的对象在接收时会产生不同的行为(即方法)。也就是说,每个对象可以用自己的方式去响应共同的消息。

  • 静态多态性(编译时的多态性)

    • 在程序编译时系统就能决定调用的是哪个函数
    • 通过函数的重载实现的(运算符重载实质上也是函数重载)。
  • 动态多态性(运行时的多态性)

    • 是在程序运行过程中才动态地确定操作所针对的对象。
    • 通过**虚函数(virtual function)**实现的

通过继承而产生了相关的不同的派生类,与基类成员同名的成员在不同的派生类中有不同的含义。也可以说,多态性是“一个接口,多种方法”。

  • 虚函数(类类型下里面讲的是虚基类)

    • 允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问**基类和派生类中的同名函数。**不用每次都加上域作用符去限定要访问的是哪一个。
  • 在类的继承层次结构中,在不同的层次中可以出现名字相同、参数个数和类型都相同而功能不同的函数。编译系统按照同名覆盖的原则决定调用的对象。
  • 人们提出这样的设想,能否用同一个调用形式,既能调用派生类又能调用基类的同名函数。在程序中不是通过不同的对象名去调用不同派生层次中的同名函数,而是通过指针调用它们。例如,用同一个语句pt->display( );可以调用不同派生层次中的display函数,只需在调用前给指针变量pt赋以不同的值(使之指向不同的类对象)即可。这并不是多态性行为(使用的是不同类型的指针),没有用到虚函数的功能。
  • 虚函数实现的动态多态性就是: 同一类族中不同类的对象,对同一函数调用作出不同的响应。

  • 当一个成员函数被声明为虚函数后,其派生类中的同名函数都自动成为虚函数。

  • 虚函数的使用方法是:

    • 在基类用virtual声明成员函数为虚函数。这样就可以在派生类中重新定义此函数,为它赋予新的功能,并能方便地被调用。在类外定义虚函数时,不必再加virtual
    • 在派生类中重新定义此函数,要求函数名、函数类型、函数参数个数和类型全部与基类的虚函数相同,并根据派生类的需要重新定义函数体
    • 定义一个指向基类对象的指针变量,并使它指向同一类族中需要调用该函数的对象。
    • 通过该指针变量调用此虚函数,此时调用的就是指针变量指向的对象的同名函数。
class Student
{
public:
[virtual] void display( );            //声明输出函数
 };
class Graduate:public Student
{
public:
   void display( ); 
};
int main()
 {
  Student stud1(1001,″Li″,87.5);                   //定义Student类对象stud1
  Graduate grad1(2001,″Wang″,98.5,563.5);          //定义Graduate类对象grad1
  Student *pt=&stud1;                              //定义指向基类对象的指针变量pt
  pt->display( );
  pt=&grad1;
  pt->display( );
  return 0;
 }
  • 当不加关键字virtual时,输出的两个结果都是基类部分的数据
  • 当加关键字virtual时,输出的是分别的display函数调用的结果
  • 函数重载
    • 处理的是同一层次上的同名函数问题,
    • 横向重载
    • 重载时函数的首部是不同的(参数个数或类型不同)。
  • 虚函数
    • 处理的是不同派生层次上的同名函数问题
    • 纵向重载。
    • 同一类族的虚函数的首部是相同的


  • 关联(binding):编译系统要根据已有的信息,对同名函数的调用作出判断。确定调用的具体对象。这里是指把一个函数名与一个类对象捆绑在一起,建立关联。

  • 静态关联(static binding):(早期关联(early binding))

    • 函数重载和通过对象名调用的虚函数,在编译时即可确定其调用的虚函数属于哪一个类。
  • 动态关联(dynamic binding):滞后关联(late binding)。

    • 运行阶段把虚函数和类对象“绑定”在一起的
    • 在运行阶段,指针可以先后指向不同的类对象,从而调用同一类族中不同类的虚函数
  • 注意

  • 只能用virtual声明类的成员函数,使它成为虚函数,而不能将类外的普通函数声明为虚函数。因为虚函数的作用是允许在派生类中对基类的虚函数重新定义。
  • 一个成员函数被声明为虚函数后,在同一类族中的类就不能再定义一个非virtual的但与该虚函数具有相同的参数(包括个数和类型)和函数返回值类型的同名函数。
  • 是否把一个成员函数声明为虚函数?
  • 成员函数所在的类是否会作为基类?成员函数在类的继承后有无可能被更改功能?如果希望更改其功能的,一般应该将它声明为虚函数。如果成员函数在类被继承后功能不需修改,或派生类用不到该函数,则不要把它声明为虚函数。
  • 对成员函数的调用是通过对象名还是通过基类指针或引用去访问? 如果是通过基类指针或引用去访问的,则应当声明为虚函数。
  • 在定义虚函数时,并不定义其函数体,即函数体是空的。它的作用只是定义了一个虚函数名,具体功能留给派生类去添加。????纯虚函数,对派生类提供共性和统一的接口。
  • 使用虚函数,系统要有一定的空间开销。当一个类带有虚函数时,编译系统会为该类构造一个虚函数表(virtual function table,简称vtable),它是一个指针数组,存放每个虚函数的入口地址。系统在进行动态关联时的时间开销是很少的,因此,多态性是高效的。


  • 虚析构函数

如果用new运算符建立了临时对象,若基类中有析构函数,并且定义了一个指向该基类的指针变量。在程序用带指针参数的delete运算符撤销对象时,会发生一个情况:** 系统会只执行基类的析构函数,而不执行派生类的析构函数。**(通过指向基类对象的指针,只能访问派生类中的基类成员,而不能访问派生类增加的成员。要用域作用符才可以。虚函数可以解决这个问题,动态绑定)

构造函数不能声明为虚函数。这是因为在执行构造函数时类对象还未完成建立过程,当然谈不上函数与类对象的绑定。

  • 当基类的析构函数为虚函数时,无论指针指的是同一类族中的哪一个类对象,系统会采用动态关联,调用相应的析构函数,对该对象进行清理工作。

  • 如果将基类的析构函数声明为虚函数时,由该基类所派生的所有派生类的析构函数也都自动成为虚函数,即使派生类的析构函数与基类的析构函数名字不相同。

  • 如果程序中显式地用了delete运算符准备删除一个对象,而delete运算符的操作对象用了指向派生类对象的基类指针,则系统会调用相应类的析构函数。

  • 纯虚函数

    • virtual 函数类型 函数名 (参数表列) =0;

    • 作用是在基类中为其派生类保留一个函数的名字,以便派生类根据需要对它进行定义。如果在基类中没有保留函数名字,则无法实现多态性。(同一个基类的不同派生类根据派生类的需要重新定义这个函数)

  • 注意:
    ① 纯虚函数没有函数体;只有函数的名字而不具备函数的功能,不能被调用。在派生类中对此函数提供定义后,它才能具备函数的功能,可被调用。
    ② 最后面的“=0”并不表示函数返回值为0,它只起形式上的作用,告诉编译系统“这是纯虚函数”;
    ③ 这是一个声明语句,最后应有分号。

  • 凡是包含纯虚函数的类都是抽象类。因为纯虚函数是不能被调用的,包含纯虚函数的类是无法建立对象的。

  • 抽象类(abstract class)不用来定义对象而只作为一种基本类型用作继承的类,由于它常用作基类,通常称为抽象基类(abstract base class)。

    • 作用是作为一个类族的共同基类,或者说,为一个类族提供一个公共接口。
    • 如果在抽象类所派生出的新类中对基类的所有纯虚函数进行了定义,这个派生类就不是抽象类,而是可以用来定义对象的具体类(concrete class)。
    • 如果在派生类中没有对所有纯虚函数进行定义,则此派生类仍然是抽象类,不能用来定义对象。
    • 可以定义指向抽象类数据的指针变量。当派生类成为具体类之后,就可以用这种指针指向派生类对象,然后通过该指针调用虚函数,实现多态性的操作。
  • 一个基类如果包含一个或一个以上纯虚函数,就是抽象基类。抽象基类不能也不必要定义对象。

  • 如果在基类声明了虚函数,则在派生类中凡是与该函数有相同的函数名、函数类型、参数个数和类型的函数,均为虚函数(不论在派生类中是否用 virtual声明)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值