C++中类的多态与虚函数的使用

http://www.cnblogs.com/fangyukuan/archive/2010/05/30/1747449.html

类的多态特性是支持面向对象的语言最主要的特性,有过非面向对象语言开发经历的人,通常对这一章节的内容会觉得不习惯,因为很多人错误的认为,支持类的封装的语言就是支持面向对象的,其实不然,Visual BASIC 6.0 是典型的非面向对象的开发语言,但是它的确是支持类,支持类并不能说明就是支持面向对象,能够解决多态问题的语言,才是真正支持面向对象的开发的语言,所以务必提醒有过其它非面向对象语言基础的读者注意!

多态的这个概念稍微有点模糊,如果想在一开始就想用清晰用语言描述它,让读者能够明白,似乎不太现实,所以我们先看如下代码:

//例程1

#include<iostream>

using namespace std;

class Vehicle

{

public:

Vehicle(float speed,int total)

{

Vehicle::speed=speed;

Vehicle::total=total;

}

void ShowMember()

{

cout<<speed<<"|"<<total<<endl;

}

protected:

float speed;

int total;

};

class Car:public Vehicle

{

public:

Car(int aird,float speed,int total):Vehicle(speed,total)

{

Car::aird=aird;

}

void ShowMember()

{

cout<<speed<<"|"<<total<<"|"<<aird<<endl;

}

protected:

int aird;

};

void main()

{

Vehiclea(120,4);

a.ShowMember();

Carb(180,110,4);

b.ShowMember();

cin.get();

}

在c++中是允许派生类重载基类成员函数的,对于类的重载来说,明确的,不同类的对象,调用其类的成员函数的时候,系统是知道如何找到其类的同名成员,上面代码中的a.ShowMember();,即调用的是Vehicle::ShowMember(),b.ShowMember();,即调用的是Car::ShowMemeber();。

但是在实际工作中,很可能会碰到对象所属类不清的情况,下面我们来看一下派生类成员作为函数参数传递的例子,代码如下:

//例程2

#include<iostream>

using namespace std;

class Vehicle

{

public:

Vehicle(float speed,int total)

{

Vehicle::speed=speed;

Vehicle::total=total;

}

void ShowMember()

{

cout<<speed<<"|"<<total<<endl;

}

protected:

float speed;

int total;

};

class Car:public Vehicle

{

public:

Car(int aird,float speed,int total):Vehicle(speed,total)

{

Car::aird=aird;

}

void ShowMember()

{

cout<<speed<<"|"<<total<<"|"<<aird<<endl;

}

protected:

int aird;

};

void test(Vehicle &temp)

{

temp.ShowMember();

}

void main()

{

Vehiclea(120,4);

Carb(180,110,4);

test(a);

test(b);

cin.get();

}

例子中,对象a与b分别是基类和派生类的对象,而函数test的形参却只是Vehicle类的引用,按照类继承的特点,系统把Car类对象看做是一个Vehicle类对象,因为Car类的覆盖范围包含Vehicle类,所以test函数的定义并没有错误,我们想利用test函数达到的目的是,传递不同类对象的引用,分别调用不同类的,重载了的,ShowMember成员函数,但是程序的运行结果却出乎人们的意料,系统分不清楚传递过来的基类对象还是派生类对象,无论是基类对象还是派生类对象调用的都是基类的ShowMember成员函数。

为了要解决上述不能正确分辨对象类型的问题,c++提供了一种叫做多态性(polymorphism)的技术来解决问题,对于例程序1,这种能够在编译时就能够确定哪个重载的成员函数被调用的情况被称做先期联编(early binding),而在系统能够在运行时,能够根据其类型确定调用哪个重载的成员函数的能力,称为多态性,或叫滞后联编(latebinding),下面我们要看的例程3,就是滞后联编,滞后联编正是解决多态问题的方法。

代码如下:

//例程3

#include<iostream>

using namespace std;

class Vehicle

{

public:

Vehicle(float speed,int total)

{

Vehicle::speed = speed;

Vehicle::total = total;

}

virtual void ShowMember()//虚函数

{

cout<<speed<<"|"<<total<<endl;

}

protected:

float speed;

int total;

};

class Car:public Vehicle

{

public:

Car(int aird,float speed,int total):Vehicle(speed,total)

{

Car::aird = aird;

}

virtual void ShowMember()//虚函数,在派生类中,由于继承的关系,这里的virtual也可以不加

{

cout<<speed<<"|"<<total<<"|"<<aird<<endl;

}

public:

int aird;

};

void test(Vehicle &temp)

{

temp.ShowMember();

}

int main()

{

Vehiclea(120,4);

Carb(180,110,4);

test(a);

test(b);

cin.get();

}

多态特性的工作依赖虚函数的定义,在需要解决多态问题的重载成员函数前,加上virtual关键字,那么该成员函数就变成了虚函数,从上例代码运行的结果看,系统成功的分辨出了对象的真实类型,成功的调用了各自的重载成员函数。

多态特性让程序员省去了细节的考虑,提高了开发效率,使代码大大的简化,当然虚函数的定义也是有缺陷的,因为多态特性增加了一些数据存储和执行指令的开销,所以能不用多态最好不用。

虚函数的定义要遵循以下重要规则:

1.如果虚函数在基类与派生类中出现,仅仅是名字相同,而形式参数不同,或者是返回类型不同,那么即使加上了virtual关键字,也是不会进行滞后联编的。

Override

2.只有类的成员函数才能说明为虚函数,因为虚函数仅适合用与有继承关系的类对象,所以普通函数不能说明为虚函数。

3.静态成员函数不能是虚函数,因为静态成员函数的特点是不受限制于某个对象。

4.内联(inline)函数不能是虚函数,因为内联函数不能在运行中动态确定位置。即使虚函数在类的内部定义,但是在编译的时候系统仍然将它看做是非内联的。

5.构造函数不能是虚函数,因为构造的时候,对象还是一片未定型的空间,只有构造完成后,对象才是具体类的实例。

6.析构函数可以是虚函数,而且通常声名为虚函数。

说明一下,虽然我们说使用虚函数会降低效率,但是在处理器速度越来越快的今天,将一个类中的所有成员函数都定义成为virtual总是有好处的,它除了会增加一些额外的开销是没有其它坏处的,对于保证类的封装特性是有好处的。

对于上面虚函数使用的重要规则6,我们有必要用实例说明一下,为什么具备多态特性的类的析构函数,有必要声明为virtual

代码如下:

#include "stdafx.h"

#include <iostream>

using namespace std;

class Vehicle

{

public:

Vehicle(float speed,int total)

{

Vehicle::speed=speed;

Vehicle::total=total;

}

virtual void ShowMember()

{

cout<<speed<<"|"<<total<<endl;

}

virtual ~Vehicle()

{

cout<<"载入Vehicle基类析构函数"<<endl;

cin.get();

}

protected:

float speed;

int total;

};

class Car:public Vehicle

{

public:

Car(int aird,float speed,int total):Vehicle(speed,total)

{

Car::aird=aird;

}

virtual void ShowMember()

{

cout<<speed<<"|"<<total<<"|"<<aird<<endl;

}

virtual ~Car()

{

cout<<"载入Car派生类析构函数"<<endl;

cin.get();

}

protected:

int aird;

};

void test(Vehicle &temp)

{

temp.ShowMember();

}

void DelPN(Vehicle *temp)

{

delete temp;

}

void main()

{

Car *a=new Car(100,1,1);

a->ShowMember();

DelPN(a);

cin.get();

}

从上例代码的运行结果来看,当调用DelPN(a);后,在析构的时候,系统成功的确定了先调用Car类的析构函数,而如果将析构函数的virtual修饰去掉,再观察结果,会发现析构的时候,始终只调用了基类的析构函数,由此我们发现,多态的特性的virtual修饰,不单单对基类和派生类的普通成员函数有必要,而且对于基类和派生类的析构函数同样重要。

C++中类的多态与虚函数的使用(转)相关推荐

  1. C++中类的多态与虚函数的使用

    C++中类的多态与虚函数的使用 http://www.cnblogs.com/fangyukuan/archive/2010/05/30/1747449.html 类的多态特性是支持面向对象的语言最主 ...

  2. 复习笔记(五)——C++多态和虚函数

    静态绑定与动态绑定 静态绑定: -编译时就能确定一条函数调用语句要调用的函数 -在程序编译时多态性体现在函数和运算符的重载上 动态绑定: -运行时才能确定函数调用语句调用的函数 -程序运行时的多态性通 ...

  3. C++初步之核心编程篇五:多态与虚函数类

    C++初步之核心编程篇五:多态与虚函数类 文章目录 C++初步之核心编程篇五:多态与虚函数类 1. 多态的概要 2. 多态案例一-计算器类 3. 纯虚函数和抽象类 4. 多态案例二-制作饮品 5. 虚 ...

  4. 【阿里面试】C++多态和虚函数

    文章目录 一.C++的面试常考点 二.阿里真题 2.1 现在假设有一个编译好的C++程序,编译没有错误,但是运行时报错,报错如下:你正在调用一个纯虚函数(Pure virtual function c ...

  5. C++ 多态、虚函数、虚方法

    子类在继承了父类的方法后,可以对父类已有的方法给出新的实现版本,这个动作称之为方法重写(override).通过方法重写我们可以让父类的同一个行为在子类中拥有不同的实现版本,当我们调用这个经过子类重写 ...

  6. PKU C++程序设计实习 学习笔记3 多态与虚函数

    第六章 多态与虚函数 6.1 多态和虚函数的基本概念 引言 多态是面向对象程序设计里面非常重要的这个机制.它能很有效的提高程序的可扩充性. 有些程序设计语言有被对象继承的概念,但是没有多态的概念,那这 ...

  7. C++学习之第十一天-多态、虚函数

    一.选择题 1.下列关于动态联编的描述中,错误的是(D). A.动态联编是以虚函数为基础 B.动态联编是运行时确定所调用的函数代码的 C.动态联编调用函数操作是指向对象的指针或对象引用 D.动态联编是 ...

  8. C++多态中虚函数的深入理解

    c++中动态多态性是通过虚函数来实现的.静态多态性是通过函数的重载来实现的,在程序运行前的一种早绑定,动态多态性则是程序运行过程中的一种后绑定.根据下面的例子进行说明. #include <io ...

  9. C++中多态与虚函数的学习例子

    多态(Polymorphism):在面向对象语言中,接口的多种不同的实现方式.也可以这样理解:在运行时,可以基类的指针来调用实现派生类中的方法.简单的一句话:允许将子类类型的指针赋值给父类类型的指针. ...

最新文章

  1. SQL经典问题1——产品、颜色、数量
  2. 生命大发的意义 BY 几米
  3. (转)OpenLayers3基础教程——OL3基本概念
  4. 如何在苹果Mac中使用聚焦搜索 NTFS 格式磁盘?
  5. PMP-项目沟通管理
  6. 版本控制软件 perforce 与 git
  7. python交集和补集的符号_python数组并集交集补集
  8. 【牛客网】马三来刷题之串的模式匹配
  9. JUnit5中@ParameterizedTest 处理 @CvsSource中的空值
  10. 揭秘中国网络虚假新闻“制造器”,看传播者如何操纵操纵大众舆论?
  11. 微信小程序:页面有内容却不显示原因
  12. 关于SI (系统集成)
  13. 【计算机视觉算法岗面经】“吐血”整理:2019秋招面经
  14. 为什么我推荐用户故事地图?
  15. 如何零成本实现微信公众号自助查券返利机器人(四)
  16. windows10家庭版打开组策略
  17. anemometer mysql_十分钟部署Anemometer作为Mysql慢查询可视化系统
  18. 应届年薪80万!这些程序员活得实在太太太爽了!
  19. 【贪心法】基站布置问题
  20. 反掩码、掩码、通配符

热门文章

  1. Python3 JSON处理
  2. Terminal(终端) 在 OS X下如何快速调用
  3. php 下载文件的函数
  4. Lua应用——tables应用,查找是否为保留字
  5. 男性在下一100层【第三层】——高仿手机银行client接口
  6. 统计决策理论2 条件分布上
  7. 从Matlab中导出不含白边的图片及设置画布尺寸及图片位置
  8. Linux shell 编程入门 - 使用ubuntu-14.10
  9. oracle第一次使用语句创建作业失败记
  10. 802.11成帧封装实现(四)