在面向对象的C++语言中,虚函数(virtual function)是一个非常重要的概念。因为它充分体现 了面向对象思想中的继承和多态性这两大特性,在C++语言里应用极广。比如在微软的MFC类库中,你会发现很多函数都有virtual关键字,也就是说, 它们都是虚函数。难怪有人甚至称虚函数是C++语言的精髓。

那么,什么是虚函数呢,我们先来看看微软的解释:

虚函数是指一个类中你希望重载的成员函数,当你用一个基类指针或引用指向一个继承类对象的时候,你调用一个虚函数,实际调用的是继承类的版本。

                                                              ——摘自MSDN

这个定义说得不是很明白。MSDN中还给出了一个例子,但是它的例子也并不能很好的说明问题。我们自己编写这样一个例子:

#include "stdio.h" #include "conio.h" 

class Parent{     public: char data[20];     void Function1(); virtual void Function2(); // 这里声明Function2是虚函数     }parent; 

void Parent::Function1() {     printf("This is parent,function1\n"); } 

void Parent::Function2() {     printf("This is parent,function2\n"); } 

class Child: public Parent { void Function1(); void Function2(); 

} child; 

void Child::Function1() {     printf("This is child,function1\n"); } 

void Child::Function2() {     printf("This is child,function2\n"); } 

int main(int argc, char* argv[]) {         Parent *p; // 定义一个基类指针     

if ( _getch()=='c' ) // 如果输入一个小写字母c         p=&child; // 指向继承类对象     else         p=&parent; // 否则指向基类对象 

    p->Function1(); // 这里在编译时会直接给出Parent::Function1()的 入口地址。        p->Function2(); // 注意这里,执行的是哪一个Function2? 

return 0; }

用任意版本的Visual C++或Borland C++编译并运行,输入一个小写字母c,得到下面的结果:

This is parent,function1 
This is child,function2

  为什么会有第一行的结果呢?因为我们是用一个Parent类的指针调用函数Fuction1(),虽然实际上这个指针指向的是Child类的对象,但编译器 无法知道这一事实(直到运行的时候,程序才可以根据用户的输入判断出指针指向的对象),它只能按照调用Parent类的函数来理解并编译,所以我们看到了 第一行的结果。

  那么第二行的结果又是怎么回事呢?我们注意到,Function2()函数在基类中被virtual关键字修饰,也就是 说,它是一个虚函数。虚函数最关键的特点是“动态联编”,它可以在运行时判断指针指向的对象,并自动调用相应的函数。如果我们在运行上面的程序时任意输入 一个非c的字符,结果如下:

This is parent,function1 
This is parent,function2

  请注意看第二行,它的结果出现了变化。程序中仅仅调用了一个Function2()函数,却可以根据用户的输入自动决定到底调用基类中的Function2 还是继承类中的Function2,这就是虚函数的作用。我们知道,在MFC中,很多类都是需要你继承的,它们的成员函数很多都要重载,比如编写MFC应 用程序最常用的CView::OnDraw(CDC*)函数,就必须重载使用。把它定义为虚函数(实际上,在MFC中OnDraw不仅是虚函数,还是纯虚 函数),可以保证时刻调用的是用户自己编写的OnDraw。虚函数的重要用途在这里可见一斑。 
----------------------------------------------------------- 
再看下面的 
派生类的大小问题C++中虚函数和纯虚函数的概念,差别和分别存在的原因

首先:强调一个概念

定义一个函数为虚函数,不代表函数为不被实现的函数,定义它为虚函数是为了允许用基类的指针来调用子类的这个函数 
定义一个函数为纯虚函数,才代表函数没有被实现,定义他是为了实现一个接口,起到一个规范的作用,规范继承这个类的程序员必须实现这个函数。

对继承的影响: 
普通的类(没有虚函数,纯虚函数)就可以被继承,而且工作的相当好

关于这个问题有以下疑问: 
纯虚函数难道就是为了实现接口?接口存在的意义? 
我实在弄不懂,我干嘛要预先定义好?未来的事情本难料,就等有一天我的类中需要使用某个函数,在添加一个函数不就可以?

关于实例化一个类: 
有纯虚函数的类是不可能生成类对象的,如果没有纯虚函数则可以。比如:

class CA { public: virtual void fun() = 0; // 说明fun函数为纯虚函数     virtual void fun1(); }; 

class CB { public: virtual void fun(); virtual void fun1(); }; 

// CA,CB类的实现 ... 

void main() {     CA a; // 不允许,因为类CA中有纯虚函数     CB b; // 可以,因为类CB中没有纯虚函数

    ... }  

---------------------------------------------------------------

虚函数在多态中间的使用: 
多态一般就是通过指向基类的指针来实现的。 
dog mydogwangwang; 
mydogwangwang.born(); 
一定是返回“dog” 
那么 
horse myhorsepipi; 
myhorsepipi.born(); 
一定是返回“horse”

也是多态呀? 

有一点你必须明白,就是用父类的指针在运行时刻来调用子类: 
例如,有个函数是这样的:

void animal::fun1(animal *maybedog_maybehorse) {      maybedog_maybehorse->born(); } 

  参数maybedog_maybehorse在编译时刻并不知道传进来的是dog类还是horse类,所以就把它设定为animal类,具体到运行时决定了才决定用那个函数。 
也就是说用父类指针通过虚函数来决定运行时刻到底是谁而指向谁的函数。

//用虚函数 #include <iostream.h> 

class animal { public:     animal();     ~animal(); void fun1(animal *maybedog_maybehorse); virtual void born(); }; 

void animal::fun1(animal *maybedog_maybehorse) {     maybedog_maybehorse->born(); } 

animal::animal() { } animal::~animal() { } void animal::born() {     cout<< "animal"; } class dog: public animal { public:     dog();     ~dog(); virtual void born(); }; dog::dog() { } dog::~dog() { } void dog::born(){     cout<<"dog"; } 

class horse:public animal { public:     horse();     ~horse(); virtual void born(); }; 

horse::horse() { } horse::~horse() { } void horse::born(){     cout<<"horse"; } 

void main() {     animal a;     dog b;     horse c;         a.fun1(&c); } //output: horse 

//不用虚函数 #include <iostream.h> 

class animal { public:     animal();     ~animal(); void fun1(animal *maybedog_maybehorse); void born();     }; 

void animal::fun1(animal *maybedog_maybehorse) {     maybedog_maybehorse->born(); } animal::animal() { } animal::~animal() { } void animal::born() {     cout<< "animal"; } class dog: public animal { public:     dog();     ~dog(); void born(); }; 

dog::dog() { } dog::~dog() { } void dog::born(){     cout<<"dog"; } class horse:public animal { public:     horse();     ~horse(); void born(); }; horse::horse() { } horse::~horse() { } void horse::born(){     cout<<"horse"; } 

void main() {     animal a;     dog b;     horse c;         a.fun1(&c); } //output: animal 

---------------------------------------------------------------

有纯虚函数的类是抽象类,不能生成对象,只能派生。他派生的类的纯虚函数没有被改写,那么,它的派生类还是个抽象类。 
---------------------------------------------------------------

定义纯虚函数就是为了让基类不可实例化化, 
因为实例化这样的抽象数据结构本身并没有意义. 
或者给出实现也没有意义 
实际上我个人认为纯虚函数的引入,是出于两个目的:

1.为了安全.因为避免任何需要明确但是因为不小心而导致的未知的结果. 提醒子类去做应做的实现. 
2.为了效率,不是程序执行的效率,而是为了编码的效率.

转载于:https://www.cnblogs.com/stoneJin/archive/2011/10/30/2229598.html

(转)虚函数和纯虚函数区别相关推荐

  1. C++ 在继承中虚函数、纯虚函数、普通函数,三者的区别

    C++ 在继承中虚函数.纯虚函数.普通函数,三者的区别 1.虚函数(impure virtual) C++的虚函数主要作用是"运行时多态",父类中提供虚函数的实现,为子类提供默认的 ...

  2. 虚函数与纯虚函数的区别

    虚函数:为了方便使用多态特性,常常需要在基类中定义虚函数. 纯虚函数: 1.原因与虚函数相同: 2.在很多情况下,基类本身生成的对象是不合理的: 虚函数与纯虚函数的区别: 1.类里声明为虚函数的话,这 ...

  3. 虚函数和纯虚函数的区别?

    虚函数和纯虚函数的区别? 虚函数 引入原因 纯虚函数 引入原因 纯虚函数相当于接口,不能直接实例化,需要派生类来实现函数定义. 虚函数在子类里面也可以不重载的:但纯虚必须在子类去实现 一旦父类的成员函 ...

  4. 虚函数与纯虚函数区别

    虚函数为了重载和多态的需要,在基类中是由定义的,即便定义是空,所以子类中可以重写也可以不写基类中的函数! 纯虚函数在基类中是没有定义的,必须在子类中加以实现,很像java中的接口函数! 虚函数 引入原 ...

  5. 栈内存 ,堆内存区别 C++ 动态内存 == 与equal区别 复合函数奇偶性 三角函数转换公式: 虚函数和纯虚函数: C++ 中的运算符重载 数据封装,数据抽象 C++ 接口(抽象类

    目录 栈内存 ,堆内存区别 C++ 动态内存 == 与equal区别 复合函数奇偶性 三角函数转换公式: 虚函数和纯虚函数: C++ 中的运算符重载 数据封装,数据抽象 C++ 接口(抽象类): #和 ...

  6. 虚函数和纯虚函数的区别

    首先:强调一个概念 定义一个函数为虚函数,不代表函数为不被实现的函数. 定义他为虚函数是为了允许用基类的指针来调用子类的这个函数. 定义一个函数为纯虚函数,才代表函数没有被实现. 定义纯虚函数是为了实 ...

  7. C++ 虚函数和纯虚函数的区别

    首先:强调一个概念 定义一个函数为虚函数,不代表函数为不被实现的函数. 定义他为虚函数是为了允许用基类的指针来调用子类的这个函数. 定义一个函数为纯虚函数,才代表函数没有被实现. 定义纯虚函数是为了实 ...

  8. C++虚函数和纯虚函数的区别

    1.虚函数和纯虚函数可以定义在同一个类中,含有纯虚函数的类被称为抽象类,而只含有虚函数的类不能被称为抽象类. 2.虚函数可以被直接使用,也可以被子类重载以后,以多态的形式调用,而纯虚函数必须在子类中实 ...

  9. c++ 虚函数,纯虚函数的本质区别

    转载博客:https://mp.weixin.qq.com/s?__biz=MzAxNzYzMTU0Ng==&mid=2651289202&idx=1&sn=431ffd1fa ...

最新文章

  1. Postman:Postman(HTTP的测试工具)使用方法详细攻略
  2. 综合评价模型的缺点_视频/图像质量评价综述(一)
  3. 化工原理第四版课后习题答案
  4. JWT-基于token的认证方式
  5. 如何压测基于容器服务ACK托管的DUBBO服务
  6. java dib文件 加位图文件头_位图(bmp)文件格式分析
  7. 基础算法 —— 递归算法
  8. 逐帧动画与夸张表情动画
  9. mysql 日志抓取变化_MySQL慢查询日志分析提取【转】
  10. 一对一直播app大热,使用源码或自主开发一对一APP需要了解哪些技术?
  11. 中国互联网关于阿里未来预测:这盘大期如何走
  12. 017指北与游移方位惯导系统知识梳理
  13. PHP实现图片转字符画
  14. C语言学习(一)软件篇
  15. zyf整合ssm环境
  16. 《理财系列》-《穷爸爸富爸爸》
  17. 如何在 Windows 中删除运行历史记录
  18. Android 网络请求框架浅解析
  19. 科学计算机设计总结,科学计算器课程设计报告
  20. 旷世科技面试——CV岗/后端开发

热门文章

  1. JZOJ 1251. 收费站
  2. java sql封装,在Java系统中封装SQL语言的处理方法及系统的制作方法
  3. python词云代码简单_Python 简单实现标签词云
  4. 苏州市计算机音乐学会地点,苏州市音乐家协会小提琴学会成立
  5. BZOJ-2115-Xor-WC2011
  6. linux 设置变量在脚本之外可用,linux – 在ssh上运行脚本时,环境变量不可用
  7. java限频_单个用户及Ip请求频率限制思路(附java实现)
  8. 2019-11-23 Modern Family Scripts (01 “Pilot”)
  9. 世界是有生命的(通向财富自由之路学习笔记十五)
  10. 可变数组NSMutableArray