虚函数

  • 1. 前言
  • 2. 具体使用
  • 3. 虚析构
  • 4. 虚构造
  • 5. 纯虚函数

1. 前言

虚函数的概念是在类继承中提出的,为了让派生类重新实现基类的方法。当对象指针/引用指向的类型来调用具体的方法。
在基类声明中使用关键字virtual标记可以被重写的方法,派生类中可以使用该关键字,也可以不用(效果一样)。
注意:virtual关键字只需在声明中添加,在源代码中无需添加。这点和static、inline关键字一致。

virtual void printOrg();//基类声明中的虚函数

2. 具体使用

  1. 如果没有使用关键字virtual,程序将根据引用/指针类型选择方法。
    举例说明:
#include <iostream>using namespace std;
//基类源码
class COriginal
{public:COriginal();~COriginal();void printOrg();
};//基类源码
COriginal::COriginal()
{cout << "Constructor of COriginal" << endl;
}void COriginal::printOrg()
{cout << "printOrg of COriginal" << endl;
}COriginal::~COriginal()
{cout << "Destruct of COriginal" << endl;
}//派生类声明
class CDerive :public COriginal
{public:CDerive();~CDerive();void printOrg();
};//派生类源码
CDerive::CDerive()
{    cout << "Constructor of CDerive" << endl;}void CDerive::printOrg()
{cout << "printOrg of CDerive" << endl;
}CDerive::~CDerive()
{    cout << "Destruct of CDerive" << endl;}int main()
{COriginal *pOrg = new CDerive;pOrg->printOrg();delete pOrg;return 0;
}

输出:

Constructor of COriginal
Constructor of CDerive
printOrg of COriginal
Destruct of COriginal

逐行分析打印,① new派生类对象时,需要先调用基类构造函数;② 调用派生类构造函数;③ 基类方法printOrg没有使用virtual关键字修饰,即没有声明为虚函数。所以根据pOrg指针的类型调用COriginal的方法;④ 释放pOrg内存后,自动调用析构(和第三点一样,所以调用的基类析构函数)。

Q:为什么创建派生对象时需要先调用基类构造?
A:基类构造函数负责初始化继承的数据成员,派生类构造函数主要用于初始化新增的数据成员。
  1. 如果使用virtual,程序将根据引用/指针指向的对象的类型选择方法。
    举例说明
#include <iostream>using namespace std;
//基类源码
class COriginal
{public:COriginal();~COriginal();virtual void printOrg();
};//基类源码
COriginal::COriginal()
{cout << "Constructor of COriginal" << endl;
}void COriginal::printOrg()
{cout << "printOrg of COriginal" << endl;
}COriginal::~COriginal()
{cout << "Destruct of COriginal" << endl;
}//派生类声明
class CDerive :public COriginal
{public:CDerive();~CDerive();virtual void printOrg();//单纯作为派生类,该virtual关键字仅为了标识,可以不加
};//派生类源码
CDerive::CDerive()
{    cout << "Constructor of CDerive" << endl;
}void CDerive::printOrg()
{cout << "printOrg of CDerive" << endl;
}CDerive::~CDerive()
{cout << "Destruct of CDerive" << endl;
}int main()
{COriginal *pOrg = new CDerive;//指针类型为COriginal,对象类型是CDerivepOrg->printOrg();delete pOrg;return 0;
}

输出:

Constructor of COriginal
Constructor of CDerive
printOrg of CDerive
Destruct of COriginal

逐行分析打印,① new派生类对象时,需要先调用基类构造函数;② 调用派生类构造函数;③ 基类方法printOrg使用virtual关键字修饰,即声明为虚函数。所以需要根据pOrg指针指向对象的类型(new CDerive返回的类型为CDerive) 调用CDerive的方法;④ 释放pOrg内存后,自动调用析构(因为基类析构没有用虚方法,指针类型是COriginal,所以调用基类析构)。

3. 虚析构

析构函数在对象生命周期结束时被自动调用,动态联编的方式和其他方法没区别。从上面的例子可以看出,析构函数符合“如果没有使用关键字virtual,程序将根据引用/指针类型选择方法。如果使用virtual,程序将根据引用/指针指向的对象的类型选择方法。

正确的做法,需要在基类中将析构函数声明为虚方法。这样做是为了确保释放派生对象时,按正确的顺序调用析构函数。

举例说明:

#include <iostream>using namespace std;
//基类源码
class COriginal
{public:COriginal();virtual ~COriginal();virtual void printOrg();
private:int *m_pOrg;
};//基类源码
COriginal::COriginal()
{m_pOrg = new int;//就是想在堆中申请内存cout << "Constructor of COriginal" << endl;
}void COriginal::printOrg()
{cout << "printOrg of COriginal" << endl;
}COriginal::~COriginal()
{delete m_pOrg;//内存释放cout << "Destruct of COriginal" << endl;
}//派生类声明
class CDerive :public COriginal
{public:CDerive();~CDerive();virtual void printOrg();//单纯作为派生类,该virtual关键字仅为了标识,可以不加
private:int *m_pDrv;
};//派生类源码
CDerive::CDerive()
{    m_pDrv = new int;//就是想在堆中申请内存cout << "Constructor of CDerive" << endl;
}void CDerive::printOrg()
{cout << "printOrg of CDerive" << endl;
}CDerive::~CDerive()
{    delete m_pDrv;//内存释放cout << "Destruct of CDerive" << endl;
}int main()
{COriginal *pOrg = new CDerive;//指针类型为COriginal,对象类型是CDerivepOrg->printOrg();delete pOrg;return 0;
}

输出:

Constructor of COriginal
Constructor of CDerive
printOrg of CDerive
Destruct of CDerive
Destruct of COriginal

在将基类析构声明为虚方法以后,终于会根据指针指向的对象类型调用析构方法了。还是逐行分析打印,① new派生类对象时,需要先调用基类构造函数;② 调用派生类构造函数;③调用CDerive的printOrg 方法;④ 释放pOrg内存后,因为基类析构被声明为virtual,将根据pOrg指向的对象类型调用派生类析构。⑤ 该对象过期,程序调用基类析构,C++规定的,没有做特殊说明……

4. 虚构造

首先明确,虚构造是有问题的。

根据准则“如果没有使用关键字virtual,程序将根据引用/指针类型选择方法。如果使用virtual,程序将根据引用/指针指向的对象的类型选择方法。”,若基类构造被声明为虚函数,一旦指针/引用指向的对象类型是派生类,将不会调用基类构造。
举例说明:

error: constructors cannot be declared 'virtual' [-fpermissive]

直接报错了……,编译器都看不下去了。

5. 纯虚函数

作为接口类的,基类中声明中定义的方法,没有具体实现,所以要求派生类实现该方法。纯虚函数的特征是末尾有“=0”的标志,如下:

virtual void printOrg()=0;

没啥好说的,贴一下菜鸟教程纯虚函数的解释吧。

C++ 虚函数、虚析构、虚构造(原创纯手码)相关推荐

  1. C++继承、虚函数、覆盖、多态、纯虚函数

    一.什么是继承 1.当遇到问题时,先查看现有的类能够解决一部分问题,如果有则继承该类,在此类的基础上进行扩展来解决问题,以此可以缩短解决问题的时间(代码复用) 2.当遇到一个大而复杂的问题时,可以先把 ...

  2. 析构函数可以为纯虚函数吗?纯虚函数可以有函数体吗?纯虚函数需要函数体吗?

    先回答标题中中的几个问题: 析构函数可以为纯虚函数吗? yes. 纯虚函数可以有函数体吗? yes. 纯虚函数需要函数体吗? 一般来讲,如果析构函数是纯虚函数,那么析构函数必须要有函数体,如果是其它函 ...

  3. 虚函数 2 之虚函数的定义

    1.虚函数的定义 虚函数就是在基类中被关键字 virtual 说明,并在派生类中重新定义的函数. 虚函数的作用是允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的 ...

  4. 虚函数原理与虚函数表

    目录 一. 虚函数 二.虚函数原理与虚函数表 一. 虚函数 虚函数: 使用 virtual 关键字声明的函数,是动态多态实现的基础. 非类的成员函数不能定义为虚函数. 类的静态成员函数不能定义为虚函数 ...

  5. 虚函数的实质——虚函数表

    目录 虚函数的实质--虚函数表 虚函数表是什么? 虚函数表长什么样? 证明了确实存在隐藏的虚函数表指针 派生类指针转换为基类类型的实质 当基类指针指向派生类对象时,虚函数表会如何变化? 虚函数的实质- ...

  6. C++多态的原理(虚函数指针和虚函数表)

    C++多态的原理 (虚函数指针和虚函数表) 1.虚函数指针和虚函数表 2.继承中的虚函数表 2.1单继承中的虚函数表 2.2多继承中的虚函数表 3.多态的原理 4.总结 1.虚函数指针和虚函数表 以下 ...

  7. 【C++】虚函数指针和虚函数列表

    本篇文章主要来讲述,C++多态的实现原理,也就是虚函数和虚函数列表是怎么回事?它们是如何实现多态的? 虚函数概述: 首先,C++多态的实现是通过关键字virtual,有了这个关键字之后,通过继承的关系 ...

  8. C++虚函数继承与虚继承

    虚函数继承和虚继承是完全不同的两个概念. 虚函数继承是解决多态性的,当用基类指针指向派生类对象的时候,基类指针调用虚函数的时候会自动调用派生类的虚函数,这就是多态性,也叫动态编联. 虚继承就是为了节约 ...

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

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

最新文章

  1. 【jsp】jsp的内置对象(部分)
  2. Linux函数名加数字,C++ 编译器的函数名修饰规则
  3. 数据库以及后台开发之写在前面
  4. 011_html标题
  5. ubuntu 安装git
  6. 【JVM性能调优】使用jstack找出最耗CPU的java线程
  7. 利用Basic authentication 测试不同user的metadata access request
  8. Python库大全涵盖了Python应用的方方面面建议收藏留用!
  9. Python 机器学习 随机森林 天气最高温度预测任务(三)
  10. 文章之间的基本总结Activity生命周期
  11. 数组保存为灰度图_「PS抠图系列9」通道
  12. 11.PHP-FPM pool
  13. 查询优化器内核剖析第六篇:谈谈Join的顺序问题,纠正江湖偏方
  14. tensorflow2 unet加载自己的图像进行训练
  15. 计算机信息系统发生安全事故,网络安全事故报告制度
  16. java实现在线预览----poi操作word转html及03、07版本兼容问题
  17. IEEE1588精密网络同步时钟协议(PTP)-v2.0协议浅析
  18. ubuntu虚拟机上外网设置
  19. cocos2dx 3.2 学习篇之六(精灵运动,自定义运动轨迹(太极八卦))
  20. Python学习Day01

热门文章

  1. IIS中FTP登陆用户名密码都对但进不去的另一种原因
  2. NBIOT模块 ME3616 AT命令 MQTT 连接 thingsboard
  3. DL4J中文文档/开始/Eclipse DL4J示例之旅
  4. Graphx中pregel详解及具体应用分析(以最短路径为例)
  5. ruby 实现 bing 的 geocode
  6. MAC M1安装telnet
  7. 【转】VMWare+WinDbg搭建(驱动)调试环境
  8. 尘缘如梦_转载网友_天还是那么蓝
  9. python tkinter获取屏幕大小_Tkinter根窗口设置小技巧:程序启动最大化和程序窗口图标设置...
  10. Android使用Glide加载Gif慢 获取gif时间