你了解c++中的关键字virtual吗?
virtual在英文中表示“虚”、“虚拟”的含义。c++中的关键字“virtual”主要用在两个方面:虚函数与虚基类。下面将分别从这两个方面对virtual进行介绍。
1.虚函数
虚函数源于c++中的类继承,是多态的一种。在c++中,一个基类的指针或者引用可以指向或者引用派生类的对象。同时,派生类可以重写基类中的成员函数。这里“重写”的要求是函数的特征标(包括参数的数目、类型和顺序)以及返回值都必须与基类中的函数一致。如下所示:
class base
{int a,b;
public:void test(){ cout<<"基类方法!"<<endl; }virtual ~base(){}
};class inheriter:public base
{
public:void test(){ cout<<"派生类方法!"<<endl; } //重写基类方法
};
可以在基类中将被重写的成员函数设置为虚函数,其含义是:当通过基类的指针或者引用调用该成员函数时,将根据指针指向的对象类型确定调用的函数,而非指针的类型。如下,是未将test()函数设置为虚函数前的执行结果:
base *p1=new base;
base *p2=new inheriter;
p1->test(); //输出“基类方法”
p2->test(); //输出“基类方法”
在将test()函数设置为virtual后,执行结果如下:
base *p1=new base; //p1指向base类
base *p2=new inheriter; //p2指向inheriter类
p1->test(); //输出“基类方法”
p2->test(); //输出“派生类方法”
如此,便可以将基类与派生类的同名方法区分开,实现多态。
说明:
1.只需将基类中的成员函数声明为虚函数即可,派生类中重写的virtual函数自动成为虚函数;
2.基类中的析构函数必须为虚函数,否则会出现对象释放错误。以上例说明,如果不将基类的析构函数声明为virtual,那么在调用delete p2;语句时将调用基类的析构函数,而不是应当调用的派生类的析构函数,从而出现对象释放错误的问题。
3.虚函数的使用将导致类对象占用更大的内存空间。对这一点的解释涉及到虚函数调用的原理:编译器给每一个包括虚函数的对象添加了一个隐藏成员:指向虚函数表的指针。虚函数表(virtual function table)包含了虚函数的地址,由所有虚函数对象共享。当派生类重新定义虚函数时,则将该函数的地址添加到虚函数表中。无论一个类对象中定义了多少个虚函数,虚函数指针只有一个。相应地,每个对象在内存中的大小要比没有虚函数时大4个字节(32位主机,不包括虚析构函数)。如下:
cout<<sizeof(base)<<endl; //12
cout<<sizeof(inheriter)<<endl; //12
base类中包括了两个整型的成员变量,各占4个字节大小,再加上一个虚函数指针,共计占12个字节;inheriter类继承了base类的两个成员变量以及虚函数表指针,因此大小与基类一致。如果inheriter多重继承自另外一个也包括了虚函数的基类,那么隐藏成员就包括了两个虚函数表指针。
4.重写函数的特征标必须与基类函数一致,否则将覆盖基类函数;
5.重写不同于重载。我对重载的理解是:同一个类,内部的同名函数具有不同的参数列表称为重载;重写则是派生类对基类同名函数的“本地改造”,要求函数特征标完全相同。当然,返回值类型不一定相同(可能会出现返回类型协变的特殊情况)。
2.虚基类
在c++中,派生类可以继承多个基类。问题在于:如果这多个基类又是继承自同一个基类时,那么派生类是不是需要多次继承这“同一个基类”中的内容?虚基类可以解决这个问题。
简而言之,虚基类可以使得从多个类(它们继承自一个类)中派生出的对象只继承一个对象。虚继承的写法如下:
class mytest:virtual public base
{
};
base称为mytest类的虚基类。假设base还是另外一个类mytest2的虚基类,对于多重继承mytest和mytest2的子类mytest3而言,base的部分只继承了一次。如下:
class base
{int b;
public:virtual void test(){ cout<<"基类方法!"<<endl; }virtual ~base(){};
};class mytest:virtual public base
{
};class mytest2:virtual public base
{
};class mytest3:public mytest,public mytest2
{
};cout<<sizeof(mytest)<<endl; //输出12
cout<<sizeof(mytest2)<<endl; //输出12
cout<<sizeof(mytest3)<<endl; //输出16,若在base中添加一个int型成员,则输出20
mytest类与mytest2类的大小为什么是12?这是因为它们在虚继承自base类后,添加了一个隐藏的成员——指向虚基类的指针,占4个字节。而base类本身占8个字节,因此它们的大小均为12。而对非虚继承而言,是不需要这样的一个指针的。而mytest3类的大小为sizeof(base)+sizeof(mytest-base)+sizeof(mytest2-base),即16。
说明:
1.若一个类多重继承自具有同一个基类的派生类时,调用同名成员函数时会出现二义性。为了解决这个问题,可以通过作用域解析运算符澄清,或者在类中进行重新定义;
2.继承关系可能是非常繁复的。一个类可能多重继承自别的类,而它的父类也可能继承自别的类。当该类从不同的途径继承了两个或者更多的同名函数时,如果没有对类名限定为virtual,将导致二义性。当然,如果使用了虚基类,则不一定会导致二义性。编译器将选择继承路径上“最短”的父类成员函数加以调用。该规则与成员函数的访问控制权限并不矛盾。也就是说,不能因为具有更高调用优先级的成员函数的访问控制权限是"private",而转而去调用public型的较低优先级的同名成员函数。
3.纯虚函数
若一个类的成员函数被声明为纯虚函数,则意味着该类是ABC(Abstract Base Class,抽象基类),即只能被继承,而不能用来声明对象。纯虚函数通常需要在类声明的后面加上关键词“=0”。
当然,声明为纯虚函数并不意味着在实现文件中不可对其进行定义,只是意味着不可用抽象基类实现一个具体的对象。
你了解c++中的关键字virtual吗?相关推荐
- 关于Oracle数据库19c中的关键字和保留字的说明
关于Oracle数据库中的关键字和保留字的说明 官方文档节选: You cannot use Oracle SQL reserved words as nonquoted identifiers. ...
- C/C++中的关键字
转载自:https://blog.csdn.net/scmuzi18/article/details/53696778 博主:_s西瓜: 来源:CSDN; C/C++中的关键字如下: asm do i ...
- C#中using关键字的作用及其用法(转)
C#中using关键字的作用及其用法 using的用途和使用技巧. using关键字微软MSDN上解释总共有三种用途: 1.引用命名空间. 2.为命名空间或类型创建别名. ...
- C#中Abstract和Virtual的区别
转自原文 C#中Abstract和Virtual的区别 c# 中 Abstract和Virtual比较容易混淆,都与继承有关,并且涉及override的使用.下面讨论一下二者的区别: 一.Virtua ...
- c语言中关键字static_了解C ++中Static关键字的变体
c语言中关键字static Hello, folks! Today we will be unveiling another important aspect in the series of our ...
- c#中sealed关键字的使用
c#中sealed关键字 前几天给大家总结了virtual(虚拟的).abstract(抽象的).override(重写)关键字,今天就抽空把sealed(密封的)给大家总结出来,因为这四个是一连串的 ...
- C#中Abstract和Virtual使用详解
转载自: https://www.cnblogs.com/vaevvaev/p/6911788.html 在C#的学习中,容易混淆virtual方法和abstract方法的使用,现在来讨论一下二者的区 ...
- (转)C#中Abstract和Virtual
转:http://www.cnblogs.com/blsong/archive/2010/08/12/1798064.html 在C#的学习中,容易混淆virtual方法和abstract方法的使用, ...
- C#中Abstract和Virtual(转载)
在C#的学习中,容易混淆virtual方法和abstract方法的使用,现在来讨论一下二者的区别.二者都牵涉到在派生类中与override的配合使用. 一.Virtual方法(虚方法) virtual ...
最新文章
- vue2.x的小问题
- 51Nod 斜率最大
- python批处理文件_Python文件夹批处理操作代码实例
- 微信小程序,前端大梦想(七)
- FA_手工明细增加固定资产(流程)
- Git (13) -- Git 分支 -- 分支的新建与合并
- 【数据结构】1、零碎知识点集合
- python下载网络图片_Python下载网络图片方法汇总与实现
- 2010年8月刊:精选热门微博
- Springboot搭建文件服务器
- 安卓标题栏优化(1)——纪念火影--隐身术
- 波许教授的《最后一课》有感
- 分式化简结果要求_分式的化简(含答案)
- 技术人向顾问/管理者转型的推荐经典书35本
- 计算机收藏夹位于哪个磁盘,Win7收藏夹在哪个盘?查看收藏夹网址保存在哪里的方法...
- Failed to shutdown database console gracefully
- 基于simulink的转速反馈单闭环直流调速系统
- 软件测评师--第五小时 黑盒测试技术
- 婚礼常用背景音乐[推荐100首
- 使用MD5加密数据库中的用户密码介绍
热门文章
- 搜狗CEO强烈推荐,98%好评,这本深度学习宝典刷爆IT圈!
- iNode安装禁用了u盘停服务也没用
- @property python知乎_使用Mxnet进行图像深度学习训练工具 InsightFace - 使用篇, 如何一键刷分LFW 99.80%, MegaFace 98%....
- Debian Epson L455 打印机
- Word删除下划线上的文字
- 人工智能行业价值成长性分析
- 读书笔记-命令行总结
- QQ/微信如何自定义分享链接缩略图?
- python 分解变量
- 理解游戏中使用的贴图资源