虚函数
● 虚函数是指在基类内部声明的成员函数前添加关键字 virtual 指明的函数
● 虚函数存在的意义是为了实现多态,让派生类能够重写(override)其基类的成员函数
● 派生类重写基类的虚函数时,可以添加 virtual 关键字,但不是必须这么做
● 虚函数是动态绑定的,在运行时才确定,而非虚函数的调用在编译时确定
● 虚函数必须是非静态成员函数,因为静态成员函数需要在编译时确定
● 构造函数不能是虚函数,因为虚函数是动态绑定的,而构造函数创建时需要确定对象类型
● 析构函数一般是虚函数
● 虚函数一旦声明,就一直是虚函数,派生类也无法改变这一事实
下面举个例子来帮助理解:

#include<iostream>
using namespace std;
class Base {public:
void f() { cout << "Base::f" << endl; };  //一般成员函数virtual void f1() { cout << "Base::f1" << endl; }; //虚函数private:char aa[3];
};
class Derived : public Base {public:
void f1() { cout << "Derived::f1" << endl; }; //重写基类的虚函数f1()private:char bb[3];
};int main() {Base a;
Derived b;
Base *p = &a;
Base *p0 = &b;
Base *p1 = &a;
Base *p2 = &b;
Derived *p3 = &b;p->f(); //基类的指针调用自己的基类部分 Base::f(), 打印结果为 Base::f
p0->f(); //基类的指针调用派生类的基类部分 Base::f(), 打印结果为 Base::fp1->f1(); //基类的指针调用基类自身的函数 Base::f1(), 打印结果为 Base::f1
p2->f1(); //基类的指针调用派生类重写的函数 Derived::f1(), 打印结果为 Derived::f1
p3->f1(); //派生类调用自己重写的函数 Derived::f1(),打印结果为 Derived::f1return 0;
}

虚函数工作机制
主要思路
虚函数表 + 虚表指针
具体实现
● 编译器在含有虚函数的类中创建一个虚函数表,称为vtable,这个vtable用来存放虚函数的地址。另外还隐式地设置了一个虚表指针,称为vptr,这个vptr指向了该类对象的虚函数表。
● 派生类在继承基类的同时,也会继承基类的虚函数表(暂且可以认为派生类此时包含了两个虚函数表所有的内容,具体接着看下面两种情况)。
● 当派生类重写(override)了基类的虚函数时,则会将重写后的虚函数的地址 替换掉 由基类继承而来的虚函数表中对应虚函数的地址(有点绕,多读一遍这一句就清楚了,这也是重写的含义所在吧,也就是覆盖掉基类部分虚函数的地址)。
● 若派生类没有重写,则由基类继承而来的虚函数的地址将直接保存在派生类的虚函数表中。
补充
每个类都只有一个虚函数表,该类的所有对象共享这个虚函数表,而不是每个实例化对象都分别有一个虚函数表。

虚函数与运行时多态

多态的实现主要分为静态多态和动态多态,静态多态主要是重载(overload),在编译时就已经确定;而动态多态是通过虚函数机制类实现,在运行时动态绑定。
动态多态是指基类的指针指向其派生类的对象,通过基类的指针来调用派生类的成员函数。

int main() {Base a;
Derived b; //派生类的对象
Base *p1 = &a;
Base *p2 = &b; //基类的指针,指向派生类的对象
p1->f1();
p2->f1(); //基类的指针调用派生类重写的虚函数 Derived::f1(), 打印结果为 Derived::f1return 0;
}

如果基类通过引用或者指针调用的是非虚函数,无论实际的对象是什么类型,都执行基类所定义的函数。即:

int main() {Base a;
Derived b;
Base *p = &a; //基类的指针,指向基类的对象
Base *p0 = &b; //基类的指针,指向派生类的对象//f()是非虚函数,所以无论指向是基类的对象a还是派生类的对象b,执行的都是基类的函数f()
p->f();
p0->f();
return 0;
}
现在可以理解什么是运行时多态了。
C++类的多态性是通过虚函数来实现的。如果基类通过引用或指针调用的是虚函数时,我们并不知道执行该函数的对象是什么类型的,只有在运行时才能确定调用的是基类的虚函数还是派生类中的虚函数,这就是运行时多态。
int main() {Base a;
Derived b;
Base *p1 = &a; //基类的指针,指向基类的对象
Base *p2 = &b; //基类的指针,指向派生类的对象//f1()是虚函数,只有运行时才知道真正调用的是基类的f1(),还是派生类的f1()
p1->f1();    //p1指向的是基类的对象,所以此时调用的是基类的f1()
p2->f1();    //p2指向的是派生类的对象,所以调用的是派生类重写后的f1()return 0;
}

与静态函数的区别
静态函数在编译时已经确定,而虚函数是在运行时动态绑定的。
虚函数因为用了虚函数表的机制,所以在调用的时候会增加一次内存开销。

纯虚函数
● 纯虚函数只在基类中声明,但没有定义,因此没有函数体。
● 纯虚函数的声明只需在虚函数形参列表后面添加 =0 即可。
● 含有纯虚函数的类都是抽象类。
● 只含有纯虚函数的类称为接口类。

含有纯虚函数的类称为抽象类(注意!!只要含有就是)。什么是抽象类?它有以下几个特点:
● 抽象类不能实例化对象。
● 抽象类的派生类也可以是抽象类(会继承),也可以通过实现全部的纯虚函数使其变成非抽象类,从而可以实例化对象。
● 抽象类的指针可以指向其派生类对象,并调用派生类对象的成员函数。
举个例子,在基类Cat中有两个纯虚函数eat()和sleep(),基类不能直接实例化一个对象来调用这两个函数,但在其派生类CatA和CatB中,可以通过实现这两个函数,当派生类不是抽象类时,便可以实例化对象了。具体请看下面代码示例:

class Cat{public://含有纯虚函数,因此Cat为抽象类virtual void eat() = 0;
virtual void sleep() = 0;
};
class CatA : public Cat {public:
virtual void eat() { cout << "eat fish." << endl; };  //实现了eat()函数
virtual void sleep() = 0;  //仍为纯虚函数,因此CatA也是抽象类
};
class CatB : public CatA {public://两个纯虚函数都被实现,都变成一般的虚函数,因此CatB不是抽象类
virtual void eat() { cout << "eat fish." << endl; };
virtual void sleep() { cout << "sleep for a long time." << endl; };
};int main() {Cat a; //报错,Cat是抽象类,不能实例化对象
CatA A; //报错,CatA也是抽象类,不能实例化对象
CatB B; //正确,CatB不是抽象类
CatB *p1 = &B;
CatA *p2 = &B; //抽象类虽然不能实例化对象,但是可以声明其指针或引用
p1->eat(); //打印出 eat fish.
p1->sleep(); //打印出 sleep for a long time.
p2->eat(); //打印出 eat fish.
p2->sleep(); //打印出 sleep for a long time.return 0;
}

● 只含有纯虚函数的类称为接口类。(注意!!是只含有)
● 接口类没有任何数据成员,也没有构造函数和析构函数。
● 接口类的指针也可以指向其派生类对象

C++: 虚函数 / 纯虚函数相关推荐

  1. 面试中常被问到(11)虚函数/纯虚函数

    虚函数 如何定义一个虚函数?在基类成员函数前加入virtual关键字,但并不代表此函数不被实现,只是说明允许基类指针调用派生类重写的此函数 一个类只要声明有虚函数或者从基类继承了虚函数,在编译过程中就 ...

  2. 什么是纯虚函数 纯虚函数的作用 如何定义使用纯虚函数

    什么是纯虚函数 纯虚函数的作用 如何定义使用纯虚函数   一 定义: 纯虚函数是一种特殊的虚函数,它的一般格式如下:  class <类名>  {  virtual <类型>& ...

  3. 【C++ 语言】面向对象 ( 继承 | 重写 | 子类调用父类方法 | 静态多态 | 动态多态 | 虚函数 | 纯虚函数 )

    文章目录 类的继承 方法的重写 子类中调用父类方法 多态 虚函数 虚函数示例 纯虚函数 相关代码 类的继承 1. 继承表示 : C++ 中继承可以使用 ":" 符号 , 格式为 & ...

  4. C++ 虚函数 纯虚函数 抽象类 代码示例

    其中抽象类指的是在类的定义中出现了纯虚函数,导致无法实例化,. 代码:头文件 #include "stdafx.h"using namespace std;class Person ...

  5. C++多态虚函数/纯虚函数demo

    #include <iostream> using namespace std;class A //A是一个抽象类,不能new A直接实例化. { public:virtual void ...

  6. C++:多态与虚函数,纯虚函数

    1.多态的机制与虚函数的机制 1.1 多态的机制 1.当在类中使用virtual声明一个函数为虚函数时,在编译时,编译器会自动在基类中默默地安插一个虚函数表指针,同时的.rodata段为这类生成一张虚 ...

  7. 虚函数 纯虚函数 虚基类说明

    原文:http://www.cnblogs.com/ms-frank/archive/2008/01/16/1041310.html 虚基类 在说明其作用前先看一段代码 [cpp] view plai ...

  8. 38.【C++ 虚函数 纯虚函数 虚基类 (最全详解)】

    虚函数.虚基类.纯虚函数 (一).虚函数 1.什么是虚函数: 2.虚函数的格式: 3.关于虚函数的注意事项: 4.虚函数的作用: 5.虚函数访问格式 6.虚函数的各种疑难杂症 [当指针是基类.但虚函数 ...

  9. 但并不从包含函数声明的接口派生_C++的虚函数和纯虚函数

    虚函数:类成员函数前面添加virtual关键字,则该函数被称为虚函数. 纯虚函数:在虚函数的基础上,在函数末尾加上 = 0. class Animal {public: virtual void Sh ...

最新文章

  1. 来!说说你在流量控制方面的经验!
  2. 解决jre生成错误的问题
  3. CentOS 5.X 开机启动流程
  4. linux shell之字符串的比较
  5. c语言程序滞留,c语言有个可以使程序延时的语句是什么?
  6. python数据框的横向贾总_[Spark][Python]DataFrame的左右连接例子
  7. matlab 提取图像轮廓(图像边缘提取)
  8. c++ 三角函数_精准备考 | 初中数学三角函数知识点全归纳
  9. [3]2020-IEEE Access-Batch Active Learning With Two-Stage Sampling 论文笔记
  10. ajax方法参数详解
  11. Base64 | Base32 | Base16编码和解码小结
  12. 计算机基础考试题及答案多选,2016年计算机一级考试PS及基础多选模拟试题及答案...
  13. WinCE下3G模块的调试
  14. 从身份证号获取身份证信息
  15. 亚马逊影响者红人,用关联视频给卖家带来哪些好处?
  16. UVa 10813 - Traditional BINGO
  17. 微信小程序利息计算器
  18. 超简单使用华为云托管服务
  19. 为什么看P1dB压缩,而不是2dB,3dB压缩
  20. 7-1 过河 (15分) Java实现

热门文章

  1. android html5 video 格式,关于H5 video在移动端播放问题
  2. 微信小程序 自定义表格
  3. 服务器之宝塔面板安装
  4. 4AI混合工具 扩展
  5. cookie注销的几种方法与注意事项
  6. IPVS load balancing
  7. CentOS下修改MySQL密码
  8. Android 12 启动画面-SplashScreen,面试看这个就够了
  9. P1496 火烧赤壁
  10. 30个 DotNet网站