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;
}

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

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • JSON转String转JSONArray-猿气满满-CSDN博客「建议收藏」

    JSON转String转JSONArray-猿气满满-CSDN博客「建议收藏」String转JSONStringjsonStr="{status:200,massage:0,result:{id:0987,name:"csdn"}}";//转jsonJSONObjectjson=JSONObject.fromObject(jsonStr);JSON转JSONArrayJSONObjectjson=JSONObject.fromObject…

  • 树莓派配置记录——aria2

    aria2是linux下的一个下载利器,支持http/BT/磁力连。本身是命令行程序,支持rpc连接,因此可以编程控制,github上有很多优秀的webUI,非常适合树莓派。aria2本身的配置选项很多,完整的列表在这里下面是我的配置,放在~/.aria2/aria2.config文件中#默认下载路径dir=/home/pi/Downloads#下载前预创建文件,ext4可…

  • python中文件的写入_python列表写入txt文件

    python中文件的写入_python列表写入txt文件1.自己写入txt直接上核心代码:withopen(“douban.txt”,”w”)asf:f.write(“这是个测试!”)12这句话自带文件关闭功能,所以和那些先open再write再close的方式来说,更加pythontic!结果就是这样:2.将文件输入(print)的内容写入txt我并不喜欢手写字符,更多时候用到的就是将程序跑出来的print写到txt中保存,比如

  • 自学java心路历程(学了半年。。。直到更久。。。。。)[通俗易懂]

    自学java心路历程(学了半年。。。直到更久。。。。。)[通俗易懂]自学背景环境:我是91年的。之前在小贷行业,混了快四年,经历过3家公司倒闭,在找工作发现没什么特技,太难。毕业都近4年了。但是我觉得必须要有所改变,要学。然后听了朋友的意见,不去培训学校自己自学。自学过程:自学的是java,18年三月份的时候因为大学学过2级C语言。java基础部分是在网上找到,大概都能看懂进去,一天看个7,8个小时都OK,但是比较少敲代码,主要学的是javase。java基…

  • switch流控(flow control)

    switch流控(flow control)半双工:在switch中又叫做背压(BackPressure)添加链接描述。在交换机进行数据转发时,同一时间内,只能发送或者接受数据。利用冲突检测机制在端口接受数据时,抑制本端口发送。全双工:switch可以在转发时,同时接收和发送数据。1.pause帧2.非对称流控3.pause时间交换机在进行内部数据转发的时,内部需要一个packagebuff进行数据缓存,在多个端口同时向…

  • 软件著作权的申请超详细图文怎么办_牛顿第二定详细内容图文

    软件著作权的申请超详细图文怎么办_牛顿第二定详细内容图文将自己申请软件著作权的申请流程记录下来,以备后用。以前软件著作权申请需要交费手续费,但是去年的出台了新的政策,软件著作权申请不在需要手续费用,尽管两三百元,但这也是一个福音,将自己申请软件著作权的流程记录下来;软件著作权的好处不言而喻,还是有一定含金量的,在说又不收费了,何乐不为?一、网址以及账号注册登录1.打开浏览器输入中国版权保护中心,以前是可以直接进入旧版网站的……

发表回复

您的电子邮箱地址不会被公开。

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