C++中virtual关键字的用法

关于virtual关键字的用法总结如下,有错误或者总结不到位的情况请能帮本人指出,非常感谢!

Virtual是C++ OO机制中很重要的一个关键字。只要是学过C++的人都知道在类Base中加了Virtual关键字的函数就是虚拟函数。

基类的函数调用如果有virtual则根据多态性调用派生类的,如果没有virtual则是正常的静态函数调用,还是调用基类的

1、虚函数的应用

看下面的一段代码的输出结果:

class Base
{
public:Base(){}
public:
       virtual void print(){cout<<"Base";}
};
 
class Derived:public Base
{
public:Derived(){}
public:
       void print(){cout<<"Derived";}
};
 
int main()
{
       Base *point=new Derived();
       point->print();
}

Output:
Derived 
这也许会使人联想到函数的重载,但稍加对比就会发现两者是完全不同的:
(1)重载的几个函数必须在同一个类中;
覆盖的函数必须在有继承关系的不同的类中
(2)覆盖的几个函数必须函数名、参数、返回值都相同;
重载的函数必须函数名相同,参数不同。参数不同的目的就是为了在函数调用的时候编译器能够通过参数来判断程序是在调用的哪个函数。这也就很自然地解释了为什么函数不能通过返回值不同来重载,因为程序在调用函数时很有可能不关心返回值,编译器就无法从代码中看出程序在调用的是哪个函数了。
(3)覆盖的函数前必须加关键字Virtual;
重载和Virtual没有任何瓜葛,加不加都不影响重载的运作。

再看下面林瑞博士讲解的一段关于关键字Virtual的用法

#include <iostream.h>
class Base
{
public:
virtual void f(float x){ cout << "Base::f(float) " << x << endl; }
void g(float x){ cout << "Base::g(float) " << x << endl; }
void h(float x){ cout << "Base::h(float) " << x << endl; }
};
 
class Derived : public Base
{
public:
virtual void f(float x){ cout << "Derived::f(float) " << x << endl; }
void g(int x){ cout << "Derived::g(int) " << x << endl; }
void h(float x){ cout << "Derived::h(float) " << x << endl; }
};
 
void main(void)
{
Derived d;
Base *pb = &d;
Derived *pd = &d;
// Good : behavior depends solely on type of the object
pb->f(3.14f); // Derived::f(float) 3.14
pd->f(3.14f); // Derived::f(float) 3.14
// Bad : behavior depends on type of the pointer
pb->g(3.14f); // Base::g(float) 3.14
pd->g(3.14f); // Derived::g(int) 3 (surprise!)
// Bad : behavior depends on type of the pointer
pb->h(3.14f); // Base::h(float) 3.14 (surprise!)
pd->h(3.14f); // Derived::h(float) 3.14
}
 
bp 和dp 指向同一地址,按理说运行结果应该是相同的,而事实上运行结果不同,所以他把原因归结为C++的隐藏规则,其实这一观点是错的。决定bp和dp调用函数运行结果的不是他们指向的地址,而是他们的指针类型。“只有在通过基类指针或引用间接指向派生类子类型时多态性才会起作用”(C++ Primer 3rd Edition)。pb是基类指针,pd是派生类指针,pd的所有函数调用都只是调用自己的函数,和多态性无关,所以pd的所有函数调用的结果都输出Derived::是完全正常的;pb的函数调用如果有virtual则根据多态性调用派生类的,如果没有virtual则是正常的静态函数调用,还是调用基类的,所以有virtual的f函数调用输出Derived::,其它两个没有virtual则还是输出Base::很正常啊,nothing surprise! 
所以并没有所谓的隐藏规则,虽然《高质量C++/C 编程指南》是本很不错的书,可大家不要迷信哦。记住“只有在通过基类指针或引用间接指向派生类子类型时多态性才会起作用”。

2、纯虚函数
纯虚函数定义如下:

C++语言为我们提供了一种语法结构,通过它可以指明,一个虚拟函数只是提供了一个可被子类型改写的接口。但是,它本身并不能通过虚拟机制被调用。这就是纯虚拟函数(purevirtual function)。 纯虚拟函数的声明如下所示:
class Query {
public:
// 声明纯虚拟函数
virtual ostream& print( ostream&=cout ) const = 0;
// ...
};
这里函数声明后面紧跟赋值0。

包含一个或多个纯虚拟函数的类被编译器识别为抽象基类。抽象基类不能被实例化,一般用于继承。抽象基类只能作为子对象出现在后续的派生类中

3、 虚拟继承(virtual public)

在多继承下,虚继承就是为了解决菱形继承中,B,C都继承了A,D继承了B,C,那么D关于 A的引用只有一次,而不是 普通继承的 对于A引用了两次……

格式:可以采用public、protected、private三种不同的继承关键字进行修饰,只要确保包含virtual就可以了。

class A
{void f1(){};
};
class B : public virtual  A{void f2(){};
};
虚继承:在继承定义中包含了virtual关键字的继承关系;
虚基类:在虚继承体系中的通过virtual继承而来的基类,

#include 
using namespace std;
class Person{
   public:    Person(){ cout<<"Person构造"<<ENDL; }
           ~Person(){ cout<<"Person析构"<<ENDL; }
};
class Teacher : virtual public Person{
   public:    Teacher(){ cout<<"Teacher构造"<<ENDL; }
            ~Teacher(){ out<<"Teacher析构"<<ENDL; }
};
class Student : virtual public Person{
  public:      Student(){ cout<<"Student构造"<<ENDL; }
             ~Student(){ cout<<"Student析构"<<ENDL; }
};
class TS : public Teacher,  public Student{
public:            TS(){ cout<<"TS构造"<<ENDL; }
                 ~TS(){ cout<<"TS析构"<<ENDL; }
};
int main(int argc,char* argv[])
{
TS ts;
return 0;
}

这段代码的终端输出结果为:
Person构造
Teacher构造
Student构造
TS构造
TS析构
Student析构
Teacher析构
Person析构
当Teacher类和Student类没有虚继承Person类的时候,也就是把virtual去掉时候终端输出的结果为:
Person构造
Teacher构造
Person构造
Student构造
TS构造
TS析构
Student析构
Person析构
Teacher析构
Person析构

     大家可以很清楚的看到这个结果明显不是我们所期望的。我们在构造TS的时候需要先构造他的基类,也就是Teacher类和Student类。而Teacher类和Student类由都继承于Person类。这样就导致了构造TS的时候实例化了两个Person类。同样的道理,析构的时候也是析构了两次Person类,这是非常危险的,也就引发出了virtual的第三种用法,虚析构。

关于虚继承的相关功能,本人也是一知半解,后续再做深入的研究

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/464474.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

很不错的Windows 控件 Developer Express Inc.NET

这个是我用 Developer Express Inc.NET 做的小东东&#xff01;很漂亮哦&#xff5e;&#xff01;文件太大了没办法上传&#xff0c;喜欢的可以去 http://www.dnc.com.cn 下载 还有注册机哦&#xff01; 转载于:https://www.cnblogs.com/xnet/archive/2005/12/21/301768.html

人生中的第一个博客

希望通过这几个月的学习&#xff0c;能够把Python掌握&#xff0c;最起码能够找到一份好的工作&#xff0c;通过自己的努力让代码改变世界&#xff01;转载于:https://www.cnblogs.com/nandadao/p/9904378.html

助你成为嵌入式高手的100多个软硬件开源项目

大家好&#xff0c;转发一个朋友总结的资料文章&#xff0c;内容如下大家好&#xff0c;我是老温&#xff0c;今天给大家推荐一个嵌入式开源项目汇总的仓库。学习初期最难找的就是找学习资料了&#xff0c;本贴精心汇总了一些嵌入式相关资源&#xff0c;包括但不限于编程语言、…

oracle联合主键怎么找,Oracle数据库联合主键

1、定义&#xff1a;主键&#xff1a;在Oracle中&#xff0c;主键指能唯一标识一条记录的单个数据表列或联合的数据表列(联合主键|复合主键)。主键用到的数据 表列数据不能包含空值。而且&#xff0c;一张表只能包含一个主键。2、作用&#xff1a;数据表的联…

笔记--相册

function moveleft(id)//图片左移 { var d document.getElementById(piclist); a eval(70); d.scrollLeft a; } function moveright()//图片右移 { var d document.getElementById(piclist); a eval(68); d.scrollLeft - a; } function showBigPic(src)…

工作杂感

其实今天算是平淡无奇的一天但不平淡的只是今天又跟老板谈话了年末嘛&#xff0c;谈话总是正常的自然而然谈到年终奖&#xff0c;加工资&#xff0c;待遇等等事先听到风声&#xff0c;知道涨幅分几等当知道自己的涨幅&#xff0c;已经算是公司数一数二的&#xff0c;已不能去反…

C++/C中mutalbe与volatile的详解

1、 mutalbe的中文意思是“可变的&#xff0c;易变的”&#xff0c;跟constant&#xff08;既C中的const&#xff09;是反义词。在C中&#xff0c;mutable也是为了突破const的限制而设置的。被mutable修饰的变量(mutable只能由于修饰类的非静态数据成员)&#xff0c;将永远处于…

没有mmu可以跑Linux吗?

为什么需要mmu?我们知道应用程序是不能随意访问内存的&#xff0c;如果让应用程序直接访问物理内存&#xff0c;那么计算机是很危险的&#xff0c;计算机内存的所有内容将被完全暴露出来。所以出现了mmu&#xff0c;mmu是内存管理单元&#xff0c;应用程序访问的是虚拟内存&am…

pC机OracLe库磁盘坏如何恢复,电脑硬盘坏了数据能恢复吗(6步教你自己在家轻松恢复数据)...

电脑硬盘毁坏如何恢复数据有过硬盘损坏经历的用户都知道&#xff0c;硬盘损坏其最严重的结果就是导致其中存储的文件难以读取&#xff0c;包括无法复制&#xff0c;无法移动。通常来说&#xff0c;具体的表现形式是复制文件或者传输文件的时候&#xff0c;耗费很长的时间都无法…

又重新让aspspider.net支持中文PDF输出了

经过一番努力&#xff0c;再次看到中文PDF的输出&#xff1b;aspspider.net经过这次更新后&#xff0c;不知道什么原因&#xff0c;使用BaseFont.AddToResourceSearch(MapPath(".\itextsharpasian\") "iTextAsian-1.0.dll");的方法就是不能把iTextAsian-1.…

My new iMac 27

iMac 27 12GB Memory. 用来贴补家用&#xff0c;有需要开发项目的可以与我联系 asp.net、组件开发 ,java,javascript,xml/xslt,objective-c 什么都行. 目前上架产品包括&#xff1a;少儿有声阅读系列《单刀会》《白蛇传》《空城记》&#xff0c;新闻阅读有 “辽宁日报” “辽宁…

url编码函数encodeURI和encodeURIComponent

var url "http://www.wrox.com/illegal value.html#start";encodeURIComponent(url) //"http%3A%2F%2Fwww.wrox.com%2Fillegal%20value.html%23start" encodeURI(url) //"http://www.wrox.com/illegal%20value.html#start" 记住2种特殊字符&…

C语言面试题大汇总之华为面试题

该面试题大全可以很好地提高C/C程序员的技术综合实力 1、局部变量能否和全局变量重名&#xff1f;  答&#xff1a;能&#xff0c;局部会屏蔽全局。要用全局变量&#xff0c;需要使用"::" ;局部变量可以与全局变量同名&#xff0c;在函数内引用这个变量时&#xff0…

从没想过会有一个这样的机会|大疆招聘

我是昨晚才知道&#xff0c;年前有一个读者一直在找工作最近准备入职大疆&#xff0c;之前我也有给他推荐了机会&#xff0c;不过都没有合适的。昨晚突然给我发消息说拿到了大疆的岗位&#xff0c;待遇和职位都是自己喜欢的。我的微信好友有好几个大疆的同学&#xff0c;刚好昨…

oracle不同session共享变量,SpringMVC使用oracle配置session共享

简述由于开发是单机项目&#xff0c;但是到了配置负载均衡的集群中就发生了问题&#xff0c;使用账号登录后又跳回主页面。我分析了原因是负载均衡配置的跳转是每次访问都会切换服务&#xff0c;所以在A机器登录后&#xff0c;再次访问B机器找不到有登录状态的session&#xff…

Google Earth 和MS Virtual Earth:虚拟地球简介

自从Goal在98年提出数字地球&#xff08;Digital Earth&#xff09;之后&#xff0c;全球掀起了数字地球热&#xff0c;而随着Goal的竞选总统的失利和当时科学技术和硬件水平的限制&#xff0c;数字地球一直处于慢慢发展状态。其中它涉及到很多学科包括地理对象的描述、存储、建…

转些别人收集的歌

2019独角兽企业重金招聘Python工程师标准>>> 【Anaesthesia】Maximilian Hecker强烈推荐 【Summer Days In Bloom】Maximilian Hecker力推&#xff01; 【end of May】Keren Ann 【gotta have you】The Weepies调调很特别&#xff0c;我用它做过背景音乐。 …

Linux社区关于链表的bug讨论我们要看一下

最近在Linux社区看到一个关于内核链表的讨论原文讨论链接&#xff1a;https://lwn.net/SubscriberLink/885941/01fdc39df2ecc25f/先用例子说明怎么使用内核链表list.h/* SPDX-License-Identifier: GPL-2.0 */ #ifndef LIST_H #define LIST_H/** Copied from include/linux/...*…

多图上传乱序php,discuz图片顺序混乱解决方案_php技巧

说明discuz在发表帖子的时候&#xff0c;添加多张图片&#xff0c;然后直接发表帖子&#xff0c;图片顺序有时候会乱掉即使上传图片窗口中图片顺序正确&#xff0c;发布之后还是会乱掉分析看url&#xff0c;程序代码中看不出什么将图片名改为序号上传&#xff0c;顺序乱了&…

在.NET中excel导出方法汇总(收集)

http://search.csdn.net/Expert/topic/2346/2346423.xml?temp.3901941http://search.csdn.net/Expert/topic/2387/2387301.xml?temp3.222293E-02http://search.csdn.net/Expert/topic/2581/2581246.xml?temp.9223444http://search.csdn.net/Expert/topic/2414/2414749.xml?…