VC++ 之 多态性与虚函数

多态性是面向对象程序设计的关键技术之一。利用多态性技术,可以调用同一个函数名的函数,实现完全不同的功能。若程序设计语言不支持多态性,不能称为面向对象的语言。


在C++中有两种多态性:

  • 编译时的多态性:通过函数的重载和运算符的重载来实现的。
  • 运行时的多态性:在程序执行前,无法根据函数名和参数来确定该调用哪一个函数,必须在程序执行过程中,根据具体情况来动态地确定。它是通过类继承关系和虚函数来实现的,目的也是建立一种通用的程序。

虚函数的定义

◆ 1、定义格式
虚函数是一个类的成员函数,定义格式如下:
    virtual 返回类型 函数名(参数表);

说明:

  • 关键字virtual指明该成员函数为虚函数。virtual仅用于类定义中,如虚函数在类外定义,不可加virtual。
  • 当某一个类的一个类成员函数被定义为虚函数,则由该类派生出来的所有派生类中,该函数始终保持虚函数的特征。
  • 当在派生类中重新定义虚函数(overriding a virtual function,亦译作超载或覆盖)时,不必加关键字virtual。但重新定义时不仅要同名,而且它的参数表和返回类型全部与基类中的虚函数一样,否则联编时出错。
  • 虚函数与“产生派生类”的第二步——改造类成员,同名覆盖(override)相区别:如未加关键字virtual,则是普通的派生类中的新成员函数覆盖基类同名成员函数(当然参数表必须一样,否则是重载),可称为同名覆盖函数,它不能实现运行时的多态性。

◆ 2、通过虚函数实现多态性
虚函数怎样实现多态性?请参见下面2个例子。

①【例8.6】计算学分。可由本科生类派生出研究生类,但它们各自的从课程学时数折算为学分数的算法是不同的,本科生是16个学时一学分,而研究生是20个学时一学分。赋值兼容规则与自定义的复制构造函数。

#include<iostream>
#include<string>
using namespace std;
class Student{
    string coursename;            //课程名
    int classhour;                    //学时
    int credit;                        //学分,未考虑0.5学分
public:
    Student(){coursename="#";classhour=0;credit=0;}
    virtual void Calculate(){credit=classhour/16;}
    void SetCourse(string str,int hour){
        coursename=str;
        classhour=hour;
    }
    int GetHour(){return classhour;}
    void SetCredit(int cred){credit=cred;}
    void Print(){cout<<coursename<<‘\t‘<<classhour<<"学时"<<‘\t‘<<credit<<"学分"<<endl;}
};
class GradeStudent:public Student{
public:
    GradeStudent(){};
    void Calculate(){SetCredit(GetHour()/20);}
};
int main(){
    Student s,*ps;
    GradeStudent g;
    s.SetCourse("物理",80);
    s.Calculate();
    g.SetCourse("物理",80);
    g.Calculate();
    cout<<"本科生:"<<‘\t‘;
    s.Print();
    cout<<"研究生:"<<‘\t‘;
    g.Print();
    s.SetCourse("数学",160);
    g.SetCourse("数学",160);
    ps=&s;
    ps->Calculate();
    cout<<"本科生:"<<‘\t‘;
    ps->Print();
    ps=&g;
    ps->Calculate();
    cout<<"研究生:"<<‘\t‘;
    ps->Print();
    getchar();
    return 0;
}

运行结果为:
本科生:物理 80学时 5学分
研究生:物理 80学时 4学分
本科生:数学 160学时 10学分
研究生:数学 160学时 8学分

分析:
第一行,学分是由Student类的成员函数Calculate()计算。
第二行,学分是由GradeStudent重新定义的Calculate()计算,它屏蔽了基类的同名函数。与不定义为虚函数一样,属编译时的多态性。
第三行,用的是指向Student类的对象s的指针,用的是Student类的Calculate()。
第四行,指针类型是指向基类的指针,但这里指针指向了派生类GradeStudent的对象g,按赋值兼容规则是准许的,但只能用基类的成员,可实际上用了派生类中新定义的Calculate()。这就是虚函数体现的多态性。如果不是虚函数,第四行输出是10学分。如果不使用基类指针指向派生类GradeStudent的对象g,也不能实现运行时的多态性。

②为体现虚函数的多态性的优点,可改造【例8.6】为

#include<iostream>
#include<string>
using namespace std;

class Student{
    string coursename;            //课程名
    int classhour;                    //学时
    int credit;                        //学分,未考虑0.5学分
public:
    Student(){coursename="#";classhour=0;credit=0;}
    virtual void Calculate(){credit=classhour/16;}
    void SetCourse(string str,int hour){
        coursename=str;
        classhour=hour;
    }
    int GetHour(){return classhour;}
    void SetCredit(int cred){credit=cred;}
    void Print(){cout<<coursename<<‘\t‘<<classhour<<"学时"<<‘\t‘<<credit<<"学分"<<endl;}
};
class GradeStudent:public Student{
public:
    GradeStudent(){};
    void Calculate(){SetCredit(GetHour()/20);}
};

void Calfun(Student &ps,string str,int hour){
    ps.SetCourse(str,hour);
    ps.Calculate();
    ps.Print();
}
int main(){
    Student s;
    GradeStudent g;
    cout<<"本科生:";
    Calfun(s,"物理",80);
    cout<<"研究生:";
    Calfun(g,"物理",80);//派生类对象初始化基类的引用,只有calculate()为虚函数才能实现动态的多态性
    return 0;
}

运行结果为:
本科生:物理 80学时 5学分
研究生:物理 80学时 4学分

分析:
这里没有用指针,而用了Student的引用,正如在第四章中所叙述的对编译器而言引用的处理同样是通过地址间接完成的,所以引用也可以实现运行时的多态性。加了一个Calfun()函数,使用更为方便。

◆ 3、注意事项
一个类中将所有的成员函数尽可能地设置为虚函数总是有好处的,但必须注意以下几条:

    • 派生类中定义的虚函数除必须与基类中的虚函数同名外,还必须同参数表,同返回类型。否则被认为是重载,而不是虚函数。如果基类中返回基类指针,派生类中返回派生类指针是允许的,这是一个例外。
    • 只有类的成员函数才能说明为虚函数。这是因为虚函数仅适用于有继承关系的类对象。
    • 静态成员函数,是所有同类对象共有,不受限于某个对象,不能作为虚函数。
    • 类的一个对象,可以有静态类型和动态类型,实质是相同的,但使用方式不同。实现动态多态性时,必须使用基类类型的指针变量或引用,使该指针指向该基类的不同派生类的对象,并通过该指针使用虚函数,才能实现动态的多态性。
    • 内联函数每个对象一个拷贝,无映射关系,不能作为虚函数。
    • 析构函数可定义为虚函数,构造函数不能定义虚函数,因为在构造函数时对象还没有完成实例化。在基类中及其派生类中都动态分配的内存空间时,必须把析构函数定义为虚函数,实现撤消对象时的多态性。
    • 虚函数执行速度要稍慢一些。因为,为了实现多态性,每一个派生类中均要保存相应虚函数的入口地址表,函数的调用机制也是间接实现。所以多态性总是要付出一定代价,但通用性是一个更高的目标。
    • 如果定义放在类外,virtual只能加在函数声明前面,不能(再)加在函数定义前面。正确的定义必须不包括virtual。
时间: 2024-10-13 05:30:23

VC++ 之 多态性与虚函数的相关文章

sdut 6-1 多态性与虚函数

6-1 多态性与虚函数 Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描述 通过本题目的练习可以掌握多态性的概念和虚函数的定义和使用方法 要求定义一个基类Pet,它有一个成员函数Speak()用于输出pet的叫声.;派生类Dog和Cat从基类Pet派生而来.他们从基类继承并重新改写了speak()函数,分别用于输出Dog类和Cat类的叫声.要求利用虚函数编写代码,使得程序能够输出下面的内容. 输入 无 输出 输出数据共有3行,本题

sdut 6-2 多态性与虚函数

6-2 多态性与虚函数 Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描述 通过本题目的练习可以掌握多态性的概念和虚函数的定义和使用方法 要求定义一个基类Pet,它有一个字符指针型数据成员name和一个虚成员函数Speak()用于输出pet的叫声.;派生类Dog和Cat从基类Pet派生而来.他们从基类继承并重新改写了speak()函数,分别用于输出Dog类和Cat类的叫声(具体输出内容参考示例输出).要求利用虚函数技术编写代码,使

【ThinkingInC++】67、多态性和虚函数

第十五章 多态性和虚函数 /** * 书本:[ThinkingInC++] * 功能:纯抽象类 * 时间:2014年10月6日13:10:28 * 作者:cutter_point */ #include <iostream> using namespace std; enum note {middleC, Csharp, Cflat}; //创建一个抽象类 //基类 class Instrument { public: //纯虚函数,不可以对纯虚函数进行传值方式的调用 virtual void

c++特别要点:多态性与虚函数

本来是准备在工厂模式中顺便整理.但粗略浏览了,内容还是很多,需要单独开一篇. 一.什么是多态性? 多态性可以概括为“一个接口,多种方法”. 多态与非多态的区别在于“成员函数调用地址的早绑定和晚绑定”.“早绑定”在编译期就可以确定函数的调用地址,是静态的:“晚绑定”在运行时才能确定函数的调用地址,是动态的. 多态的作用是什么呢?在面向对象的编程中,“封装”使得代码模块化:“继承”可以扩展以存在的代码:“多态”使得接口重用. 二.多态性的c++实现:虚函数 声明基类的指针,该指针指向子类的对象.调用

多态性与虚函数

多态性 多态性是面向对象程序设计的一个重要特征.如果一种语言只支持类而不支持多态,是不能被称为面向对象语言的.只能说是基于对象的,如Ada,VB就属于此类. 在C++程序设计中,多态性是指具有不同功能的函数可以用同一个函数名,这样就可以用一个函数名调用不同内容的函数. 在面向对象方法中一般是这样表述多态性的:向不同的对象发送同一个消息,不同的对象在接收时会产生不同的行为(即方法).也就是说,每个对象可以用自己的方式去相应共同的消息.所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的

C++多态性与虚函数

面向对象程序设计中的多态性是指向不同的对象发送同一个消息,不同对象对应同一消息产生不同行为.在程序中消息就是调用函数,不同的行为就是指不同的实现方法,即执行不同的函数体.也可以这样说就是实现了"一个接口,多种方法". 从实现的角度来讲,多态可以分为两类:编译时的多态性和运行时的多态性.前者是通过静态联编来实现的,比如C++中通过函数的重载和运算符的重载.后者则是通过动态联编来实现的,在C++中运行时的多态性主要是通过虚函数来实现的. 赋值兼容     不过在说虚函数之前,先介绍一个有关

第十三周阅读项目(4):多态性与虚函数

(1)代码: #include <iostream> using namespace std; class Vehicle //交通工具 { public: void run() const { cout << "run a vehicle. "<<endl; } }; class Car: public Vehicle //汽车 { public: void run() const { cout << "run a car.

C++之多态性与虚函数

面向对象程序设计中的多态性是指向不同的对象发送同一个消息,不同对象对应同一消息产生不同行为.在程序中消息就是调用函数,不同的行为就是指不同的实现方法,即执行不同的函数体.也可以这样说就是实现了“一个接口,多种方法”. 从实现的角度来讲,多态可以分为两类:编译时的多态性和运行时的多态性.前者是通过静态联编来实现的,比如C++中通过函数的重载和运算符的重载.后者则是通过动态联编来实现的,在C++中运行时的多态性主要是通过虚函数来实现的,也正是今天我们要讲的主要内容. 1.不过在说虚函数之前,我想先介

多态性与虚函数——C/C++学习笔记

多态性(polymorphism) 即:向不同对象发送同一个消息,不同的对象在接收时候会产生不同行为. 所谓消息,就是函数调用. C++中多态性就是:具有不同功能的函数可以有相同的函数名称.即实现用同一个函数名调用不同的函数内容. C++多态性分为:静态多态性 和 动态多态性 静态多态性:通过函数重载或者运算符重载实现,在编译阶段即知道函数的全部调用关系,也称为是编译时的多态性.根据表达式上下文确定该执行哪一个功能. 优点:调用速度快,效率高:缺点:缺乏灵活性. 动态多态性:在运行时实现的多态,