当前位置: 首页 > news >正文

C++虚函数虚析构函数纯虚函数的使用说明和理解

一、为什么要使用虚函数

请看下列代码:

#include <iostream>
#include <string>
using namespace std;class Student{
public:Student(int n, string na, float s):num(n),name(na),score(s){}void display( );//virtual void display( );//此时基类的成员函数被定义为虚函数!!!!
protected:int num;string name;float score;
};class Graduate:public Student
{
public:Graduate(int n, string na, float s, float p):Student(n,na,s),pay(p){}void display( );
private:float pay;
};void Student::display() 
{cout<<"num:"<<num<<"\nname:"<<name<<"\nscore:"<<score<<"\n\n";
}void Graduate::display() 
{cout<<"num:"<<num<<"\nname:"<<name<<"\nscore:"<<score<<"\npay="<<pay<<endl;
}int main( )
{Student stud1(1001,"Li",87.5);Graduate grad1(2001,"Wang",98.5,563.5);Student *pt = &stud1;//注意:此处pt由基类定义,指向基类对象的指针pt->display();pt = &grad1;pt->display();Graduate *pt1 = &grad1;//注意:此处定义pt1指向派生类对象pt1->display();return 0;
}

运行结果如下:

从以上例子可看出,由于定义了pt为指向基类Student的指针,因此该指针只能访问基类成员,而不能访问派生类成员。

但是当基类的成员函数display被定义为虚函数呢?

virtual void display( );

运行结果如下:

由上可知,当基类的成员函数被定义为虚函数时,即便指针指向基类对象,但是当赋值派生类给当前指针时,仍可以调用派生类的成员函数!

二、虚函数的使用方法

在基类中使用virtual声明成员函数为虚函数:

类内声明: virtual [类型] 函数名([参数表列])

在类外定义虚函数时,不再加virtual;

使用虚函数,可获得如下效果:

1.派生类根据需要可以重新定义函数体(函数覆盖),使用虚函数提高了程序的可扩充性;
2.成员函数被声明为虚函数后,其派生类中函数覆盖的同名函数自动成为虚函数;
3.若虚函数在派生类中未重定义,则派生类简单地继承其直接基类的虚函数;
4.指向基类的指针,当指向派生类对象时,则可以调用派生类的方法。

再举一个例子:

#include <iostream>
#include <string>
using namespace std;class Circle
{
public:Circle(double r=0):radius(r) { }double area ( ) const//virtual double area ( ) const//定义为虚函数!!!{return 3.14159*radius*radius;//圆形面积公式s = πr²}
protected:double radius;
};class Cylinder:public Circle
{
public:Cylinder (double r=0,double h=0):Circle(r),height(h) {}double area() const{return 2*Circle::area( )+2*3.14159*Circle::radius*height;//圆柱面积公式:2个底面积+1个侧面积,其中侧面积 = 2πrh};
protected:double height;
};int main( )
{Circle c1(5.2);Cylinder cy1(5.2, 10);Circle *pc=&c1;cout<<pc->area()<<endl;pc = &cy1;cout<<pc->area()<<endl;return 0;
}

由于只定义了一个指向基类Circle对象的指针pc(未定义指向派生类对象的指针),因此

基类成员函数“double area ( ) const”不定义为虚函数时的运算结果:

定义为虚函数时的运算结果:

三、静态关联和动态关联

通过关联,把一个标识符和一个存储地址联系起来;即把一个函数名与一个类对象捆绑在一起。

静态关联:函数重载和通过对象名调用的(虚)函数,在编译阶段即可确定其调用的(虚)函数属于哪一个类;

动态关联:通过基类指针虚函数,在代码运行阶段才确定关联关系;动态关联提供动态的多态性,即运行阶段的多态性。

四、何时使用虚函数

(一)何时使用

首先,如果一个类要作为基类,其成员函数可以定义为虚函数;如果不是基类,省点事吧

其次,看成员函数在类被继承后有无可能被更改功能。如果希望更改其功能的,一般应该在基类中将其声明为虚函数,如果成员函数在类被继承后功能不需修改,或派生类用不到该函数,则不声明为虚函数。

再次,应考虑对成员函数的调用是通过对象名还是通过基类指针或引用去访问。如果是通过基类指针或引用去访问的,则应当声明为虚函数。

有时,在定义虚函数时,并不定义其函数体,即函数体是空的,只等着被继承。它的作用只是定义了一个虚函数名,具体功能留给派生类去添加——纯虚函数

(二)关于虚函数的进一步理解

虚函数只能是类的成员函数,而不能将类外的普通函数声明为虚函数。

虚函数的作用是允许在派生类中基类的虚函数重新定义(函数覆盖),只能用于类的继承层次结构中。

排他性:一个成员函数被声明为虚函数后,在同一类族中的类就不能再定义非virtual,但与该虚函数具有相同的参数(包括个数和类型)和函数返回值类型的同名函数。

使用虚函数系统要有少量的空间开销:当一个类带有虚函数时,编译系统会为该类构造一个虚函数表(一个指针数组),用于存放每个虚函数的入口地址。

五、虚析构函数

(一)虚析构函数举例   

当派生类的对象从内存中撤销时,一般先调用派生类的析构函数,然后再调用基类的析构函数。问题由此而来:

#include <iostream>
#include <string>
using namespace std;class Point{
public:Point() {}~Point()//virtual ~Point()//    定义基类的析构函数为虚函数时{cout<<"executing ~Point destructor OK"<<endl;}
};class Circle:public Point
{
public:Circle(){}~Circle( ){cout<<"executing ~Circle destructor OK"<<endl;}
};int main( )
{Point *p=new Circle;delete p;return 0;
}

运行结果:

发现只执行了基类Point的析构函数,派生类Circle的析构函数未执行!

即:用new运算符建立了派生类对象,并且由一个基类的指针变量指向该派生类对象,用delete运算符撤销对象时,系统只执行基类的析构函数,而不执行派生类的析构函数——派生类对象析构中要求的工作将被忽略。

而如果基类的析构函数定义为虚函数时,即“virtual ~Point()”,执行结果如下:

由此可见,如果将基类的析构函数声明为虚函数时,由该基类所派生的所有派生类的析构函数也都自动成为虚函数,即使派生类的析构函数与基类的析构函数名字不相同。

(二)虚析构函数用法

当基类的析构函数为虚函数时,无论指针指的是同一类族中的哪一个类对象,系统会采用动态关联自动调用相应的析构函数,对该对象进行清理工作:先调用了派生类的析构函数,再调用了基类的析构函数,符合人们的愿望;

最好把基类的析构函数声明为虚函数,这将使所有派生类的析构函数自动成为虚函数;

虚析构函数是很重要的技巧:专业人员一般都习惯声明虚析构函数,即使基类并不需要析构函数,也显式地定义一个函数体为空的虚析构函数,以保证撤销动态分配空间时能正确的处理

构造函数不能声明为虚函数。

六、纯虚函数

虚函数,有时并不是基类本身的要求,而是考虑到派生类的需要,在基类中预留了一个函数名

在设计类的层次结构过程中,常常有的基类的成员函数并无意义,具体功能由派生类决定

纯虚函数是只有函数的名字而不具备函数的功能不能被调用的虚成员函数。

(一)纯虚函数的定义

纯虚函数的定义格式:

virtual 函数类型 函数名 (参数表列) =0

virtual float area()=0; //纯虚函数
virtual float area() const =0; //纯虚常函数//注意区别
virtual float area() const {return 0}; //虚函数

①纯虚函数没有函数体
②最后面的“=0” 只起形式上的作用,告诉编译系统“这是纯虚函数”;
③这是一个声明语句,最后应有分号

如果在一个类中声明了纯虚函数,而在其派生类中没有对该函数定义,则该虚函数在派生类中仍
然为纯虚函数。

(二)纯虚函数的应用

#include <iostream>
#include <string>
using namespace std;class Animal
{
public:virtual void cry() = 0; //定义基类中的纯虚函数
};class Mouse : public Animal
{
public:void cry(){cout<<"zhi zhi zhi"<<endl;}
};class Dog : public Animal
{
public:virtual void cry(){cout<<"wang wang wang"<<endl;}
};class Cat : public Animal
{
public:virtual void cry(){cout<<"miao miao miao"<<endl;}
};int main( )
{Animal *p;    //定义p为指向基类Animal的指针//p = new Animal();//p->cry();Mouse m1;p=&m1;p->cry();Cat c1;p=&c1;p->cry();Dog d1;p=&d1;p->cry();return 0;
}

运行结果:

由上可知:

纯虚函数功能的实现,只能在派生类中
 有纯虚函数的类,丧失了定义对象的能力
 有纯虚函数的类,是专门来当基类的!


文章转载自:

http://AWs95Ks8.mxnhq.cn
http://dEzzrasQ.mxnhq.cn
http://0J5DLE0J.mxnhq.cn
http://NO6F2qNj.mxnhq.cn
http://rq0SJ0vZ.mxnhq.cn
http://WsL9xMZK.mxnhq.cn
http://cgAAEzhX.mxnhq.cn
http://2CDCHuve.mxnhq.cn
http://RNByAich.mxnhq.cn
http://Hv0cWWBr.mxnhq.cn
http://nD5FranT.mxnhq.cn
http://zJqpLab5.mxnhq.cn
http://2GmSRvfr.mxnhq.cn
http://WoiTDROM.mxnhq.cn
http://cYaI21xE.mxnhq.cn
http://0xILguYl.mxnhq.cn
http://CWYGRUWf.mxnhq.cn
http://kbhjAS9r.mxnhq.cn
http://ogmLMSLf.mxnhq.cn
http://vprakvAu.mxnhq.cn
http://RIZO7czR.mxnhq.cn
http://99qfyw63.mxnhq.cn
http://S8v74mUT.mxnhq.cn
http://FacEMzhe.mxnhq.cn
http://jkE9MnvX.mxnhq.cn
http://GZYwQWUc.mxnhq.cn
http://Vn8CiwNc.mxnhq.cn
http://Py9N5SQN.mxnhq.cn
http://qIC8YOUT.mxnhq.cn
http://X7JFQdkC.mxnhq.cn
http://www.dtcms.com/a/367738.html

相关文章:

  • Process Explorer 学习笔记(第三章3.1.1):度量 CPU 的使用情况详解
  • 机器学习入门,第一个MCP示例
  • Spring Boot项目中MySQL索引失效的常见场景与解决方案
  • 2025 年高教社杯全国大学生数学建模竞赛C 题 NIPT 的时点选择与胎儿的异常判定 完整成品思路模型代码分享,全网首发高质量!!!
  • 代码随想录学习摘抄day6(二叉树1-11)
  • 吴恩达机器学习(五)
  • Web 与 Nginx 网站服务:从基础到实践
  • 为什么打印出来的 cJSON type 值和头文件定义的不一样?
  • MySQL子查询的分类讲解与实战
  • 【蓝桥杯选拔赛真题64】C++最大空白区 第十四届蓝桥杯青少年创意编程大赛 算法思维 C++编程选拔赛真题解
  • 企业中团队最常使用的git命令操作
  • MCP 和 Fuction Call 有什么不同
  • 去中心化投票系统开发教程 第一章:区块链基础知识
  • 热门盘点|政务办公移动化:开启政务服务高效协同新时代
  • ICPC Central Russia Regional Contest, 2024
  • (A题|烟幕干扰弹的投放策略)2025年高教杯全国大学生数学建模国赛解题思路|完整代码论文集合
  • 化工行业的设备管理软件应用_HawkEye智能运维平台_璞华大数据
  • 论文介绍:Fast3R,更快的多视图 3D 重建的新范式
  • Java 流程控制:从入门到面试的全方位指南
  • 嵌入式第四十六天(51单片机)
  • Dubbo消费者无法找到提供者问题分析和处理
  • ​​Nginx高性能Web服务器实战:从协议原理到运维优化​​
  • 【ffmepg+ AI 】从mp3歌曲提取伴奏(纯音乐)
  • TreeMap 和 LinkedHashMap原理介绍
  • 手写智能指针:带你彻底搞懂 C++ 内存管理的底层逻辑
  • MySQL中CASE语法规则的详细解析及扩展示例
  • 基于单片机楼宇火灾检测系统/仓库火灾检测报警系统
  • 基础crud项目(前端部分+总结)
  • 从零开始学大模型之预训练语言模型
  • 大语言模型基础-Transformer之上下文