C++学习——虚函数与纯虚函数

C++学习——虚函数与纯虚函数引言:若要访问派生类中相同名字的函数,必须将基类中的同名函数定义为 虚函数,这样,将不同的派生类对象的地址赋给基类的指针变量后, 就可以动态地根据这种赋值语句调用不同类中的函数。一、虚函数的定义和使用可以在程序运行时通过调用相同的函数名而实现不同功能的 函数称为虚函数。定义格式为:virtual FuncName();一旦把基类的成员函数定义为虚函数,由基类所派生出来的所 有派生类中,…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

引言:

若要访问派生类中相同名字的函数,必须将基类中的同名函数定义为 虚函数,这样,将不同的派生类对象的地址赋给基类的指针变量后, 就可以动态地根据这种赋值语句调用不同类中的函数。

一、虚函数的定义和使用

可以在程序运行时通过调用相同的函数名而实现不同功能的 函数称为虚函数。定义格式为:
virtual FuncName();
一旦把基类的成员函数定义为虚函数,由基类所派生出来的所 有派生类中,该函数均保持虚函数的特性。
在派生类中重新定义基类中的虚函数时,可以不用关键字
virtual来修饰这个成员函数 。
虚函数是用关键字virtual修饰的某基类中的protected或public成员函数。 它可以在派生类中重新定义,以形成不同版本。只有在程序的执行过
程中,依据指针具体指向哪个类对象,或依据引用哪个类对象,才能确定激活哪一个版本,实现动态聚束。

关于虚函数,说明以下几点:

1、当在基类中把成员函数定义为虚函数后,在其派生类中定义的虚函数必须与基类中的虚函数同名,参数的类型、顺序、参数的个数必须一一对应,函数的返回的类型也相同。若函数名相同,但参数的个数不同或者参数的类型不同时,则属于函数的重载,而不是虚函数。若函数名不同,显然这是不同的成员函数。
2、实现这种动态的多态性时,必须使用基类类型的指针变量,并使该 指针指向不同的派生类对象,并通过调用指针所指向的虚函数才能实现 动态的多态性。
3、虚函数必须是类的一个成员函数,不能是友元函数,也不能是静态 的成员函数。
4、在派生类中没有重新定义虚函数时,与一般的成员函数一样,当调 用这种派生类对象的虚函数时,则调用其基类中的虚函数。

5、可把析构函数定义为虚函数,但是,不能将构造函数定义为虚函数。
6、虚函数与一般的成员函数相比较,调用时的执行速度要慢一些。为 了实现多态性,在每一个派生类中均要保存相应虚函数的入口地址表,函数的调用机制也是间接实现的。因此,除了要编写一些通用的程序,并一定要使用虚函数才能完成其功能要求外,通常不必使用虚函数。7、一个函数如果被定义成虚函数,则不管经历多少次派生,仍将保持 其虚特性,以实现“一个接口,多个形态”。

虚函数的访问

用基类指针访问与用对象名访问的区别:
1、用基指针访问虚函数时,指向其实际派生类对象重新定义的函数。实 现动态聚束。
2、通过一个对象名访问时,只能静态聚束。即由编译器在编译的时候决 定调用哪个函数。

二、纯虚函数

基类中不对虚函数给出有意义的实现,它只是在派生类中有具体的意义。这时基类中的虚函数只是一个入口,具体的目的地由不同的派生类中的对象决定。这个虚函数称为纯虚函数。

class <基类名>
{ virtual <类型><函数名>(<参数表>)=0;

};
1、在定义纯虚函数时,不能定义虚函数的实现部分。
2、把函数名赋值为0,本质上是将指向函数体的指针值赋为初值0。与定义空函数不一样,空函数的函数体为空,即调用该函数时,不执行任何动作。没有在派生类重新定义这种虚函数之前,是不能调用这种纯虚函数的。
3、把至少包含一个纯虚函数的类,称为抽象类。这种类只能作为派生 类的基类,不能用来创建对象。
其理由是明显的:因为虚函数没有实现部分,所以不能产生对象。但 可以定义指向抽象类的指针,即指向这种基类的指针。当用这种基类 指针指向其派生类的对象时,必须在派生类中重载纯虚函数,否则会 产生程序的运行错误。
4、在以抽象类作为基类的派生类中必须有纯虚函数的实现部分,即必 须有重载纯虚函数的函数体。否则,这样的派生类也是不能产生对象的。 综上所述,可把纯虚函数归结为:抽象类的唯一用途是为派生类提供基 类,纯虚函数的作用是作为派生类中的成员函数的基础,并实现动态多 态性。

三、补充内容

const 、volatile对象和成员函数

用const修饰的对象,只能访问该类中用const修饰的成员函数,而其它的成员函数 是不能访问的。
用volatile修饰的对象,只能访问该类中用volatile修饰的成员函数, 不能访问其它的成员函数。
当希望成员函数只能引用成员数据的值,而不允许成员函数修改数据成员的值时, 可用关键词const修饰成员函数。一旦在用const修饰的成员函数中出现修改成员数 据的值时,将导致编译错误。
const和volatile成员函数
在成员函数的前面加上关键字const,则表示这函数返回一个常量,其值不可改变。

const成员函数则是指将const放在参数表之后,函数体之前.
其一般格式为:FuncName() const ;

其语义是指明这函数的this指针所指向的对象是一个常量,即规定了const成员函数 不能修改对象的数据成员,在函数体内只能调用const成员函数,不能调用其它的成 员函数。
用volatile修饰一个成员函数时,其一般格式为:
FuncName() volatile;
其语义是指明成员函数具有一个易变的this指针,调用这个函数时,编译程序把属
于此类的所有的数据成员都看作是易变的变量,编译器不要对这函数作优化工作。

由于关键字const和volatile是属于数据类型的组成部分,因此,若在类定义 之外定义const成员函数或volatile成员函数时,则必须用这二个关键字修饰,否则 编译器认为是重载函数,而不是定义const成员函数或volatile成员函数。
指向类成员的指针
在C++中可以定义一种特殊的指针,它指向类中的成员函数或类中的数据成员。并 可通过这样的指针来使用类中的数据成员或调用类中的成员函数。

指向类中数据成员的指针变量

定义一个指向类中数据成员的指针变量的一般格式为:
ClassName:: PointName;
其中type是指针PointName所指向数据的类型,它必须是类ClassName中某一数据成 员的类型
1、指向类中数据成员的指针变量不是类中的成员,这种指针变量应在类外定义。
2、与指向类中数据成员的指针变量同类型的任一数据成员,可将其地址赋给这种指针变量,赋值
的一般格式为:
PointName = &ClassName::member;
这种赋值,是取该成员相对于该类的所在对象中的偏移量,即相对地址(距离开始位置的字节数)
如:mptr = &S::y;
表示将数据成员y的相对起始地址赋给指针变量mptr。
3、用这种指针访问数据成员时,必须指明是使用那一个对象的数据成员。当与对象结合使用时, 其用法为:
ObjectName.
PointName
4、由于这种指针变量并不是类的成员,所以使用它只能访问对象的公有数据成员。若要访问对象的
私有数据成员,必须通过成员函数来实现。
指向类中成员函数的指针变量
定义一个指向类中成员函数的指针变量的一般格式为:
(ClassName:: *PointName)();
其中PointName是指向类中成员函数的指针变量;ClassName是已定义的类名;type 是通过函数指针PointName调用类中的成员函数时所返回值的数据类型,它必须与 类ClassName中某一成员函数的返回值的类型相一致;是函数的形式参 数表。
在使用这种指向成员函数的指针前,应先对其赋值
PointName= ClassName::FuncName;
同样地,只是将指定成员函数的相对地址赋给指向成员函数的指针。
在调用时,用(对象名.指针)( )的形式。
比如 :int max( int a,int b)
{
return (a>b?a:b);
}
若有:int(*f)( int, int ); f=max;
则调用时(*f)(x,y);
所以:(s1.*mptr1)(); (s1.*mptr2)(100);
或: (ps->*mptr1)( ); (ps-*mptr2)(100);

对指向成员函数的指针变量的使用方法说明以下几点:

1、指向类中成员函数的指针变量不是类中的成员,这种指针变量应在类外定义。
2、不能将任一成员函数的地址赋给指向成员函数的指针变量,只有成员函数的参 数个数、参数类型、参数的顺序和函数的类型均与这种指针变量相同时,才能将成 员函数的指针赋给这种变量。
3、使用这种指针变量来调用成员函数时,必须指明调用那一个对象的成员函数, 这种指针变量是不能单独使用的。用对象名引用。
4、由于这种指针变量不是类的成员,所以用它只能调用公有的成员函数。若要访 问类中的私有成员函数,必须通过类中的其它的公有成员函数。
5、当一个成员函数的指针指向一个虚函数,且通过指向对象的基类指针或对象的 引用来访问该成员函数指针时,同样地产生运行时的多态性。
6、当用这种指针指向静态的成员函数时,可直接使用类名而不要列举对象名。这 是由静态成员函数的特性所确定的。

例题:

通过虚函数实现一个计算矩形面积和三角形状面积的程序。基类为形状类,派生类为矩形类和三角形类,通过基类指针指向不同的派生类对象,调用求面积函数,得到相应的面积结果,实现多态性。

(注:派生类没有成员变量,计算面积使用基类成员变量)

代码如下:

class Shape
{ 
   
public:
	double s;
	double a;
	double b;
	double h;
	double virtual SS(double x, double y)
	{ 
   
		cout << "调用了基类的函数" << endl;
		return 0;
	}
};

class Rectangle:public Shape
{ 
   
public:
	double SS(double x, double y);
};

double Rectangle::SS(double x, double y)
{ 
   
	cout << "调用了Rectangle类的函数" << endl;
	a = x;
	b = y;
	s = 1.0 / 2 * (x * y);
	cout << "三角形的面积为" << s << endl;
	return 0;
}

class Triangle:public Shape
{ 
   
public:
	double SS(double x, double y);
};

double Triangle::SS(double x, double y)
{ 
   
	cout << "调用了Triangle类的函数" << endl;
	a = x;
	h = y;
	s = x * y;
	cout << "长方体的面积为"<<s<< endl;
	return 0;
}
int main()
{ 
   
	Rectangle a;
	Triangle b;
	Shape *p;
	p = &a;
	p->SS(2.6, 6.6);
	p = &b;
	p->SS(9.9, 2.0);
	
	return 0;
}

结果如下:
在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/171000.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • matlab在极坐标中绘图y=sin(6x)_极坐标中θ范围怎么求

    matlab在极坐标中绘图y=sin(6x)_极坐标中θ范围怎么求在极坐标中绘图TryThisExampleTryThisExampleTryThisExampleTryThisExampleTryThisExampleTryThi

    2022年8月5日
    4
  • wing是什么_完全二叉树的深度

    wing是什么_完全二叉树的深度设一个 n 个节点的二叉树 tree 的中序遍历为(1,2,3,…,n),其中数字 1,2,3,…,n 为节点编号。每个节点都有一个分数(均为正整数),记第 i 个节点的分数为 di,tree 及它的每个子树都有一个加分,任一棵子树 subtree(也包含 tree 本身)的加分计算方法如下:subtree的左子树的加分 × subtree的右子树的加分 + subtree的根的分数若某个子树为空,规定其加分为 1。叶子的加分就是叶节点本身的分数,不考虑它的空子树。试求一棵符合中序遍历为(1,2,

    2022年8月8日
    4
  • html背景图片自适应屏幕大小_jsp背景图片自适应

    html背景图片自适应屏幕大小_jsp背景图片自适应html图片自适应窗口大小background-size:cover会把图片拉伸至足够大,但是背景图片有些部分可能显示不全#bgImg{height:100%;width:100%;background:url(‘../img/bg/snow.jpg’)no-repeat;background-size:cover;position:absolu…

    2022年10月6日
    3
  • smartctl命令详解_fuser命令

    smartctl命令详解_fuser命令smart用法1.smartctl-son/dev/sda”开启磁盘的smart功能2.smartctl-a/dev/sda”显示磁盘的详细smart信息3.smartctl–test=short/dev/sda4.smartctl–test=short/dev/sdb检查磁盘坏道…

    2022年10月8日
    7
  • 测试音视频地址(音视频性能测试)

    在线视频地址https://media.w3.org/2010/05/sintel/trailer.mp4http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4http://vjs.zencdn.net/v/oceans.mp4https://media.w3.org/2010/05/sintel/trailer.mp4http://…

    2022年4月13日
    71
  • idea2021激活码永久【中文破解版】

    (idea2021激活码永久)这是一篇idea技术相关文章,由全栈君为大家提供,主要知识点是关于2021JetBrains全家桶永久激活码的内容IntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,下面是详细链接哦~https://javaforall.net/100143.htmlS32PGH0SQB-eyJsa…

    2022年3月26日
    75

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号