我的编程空间,编程开发者的网络收藏夹
学习永远不晚

C++ Virtual是什么

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

C++ Virtual是什么

这篇文章主要介绍“C++ Virtual是什么”,在日常操作中,相信很多人在C++ Virtual是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C++ Virtual是什么”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

Virtual是C++ OO(面向对象机制)机制中很重要的一个关键字。虚函数就是因为成员函数加了关键字virtual,可见它的重要性。

只要是学过C++的人都知道在类Base中加了Virtual关键字的函数就是虚拟函数(例如函数print),于是在Base的派生类Derived中就可以通过重写虚拟函数来实现对基类虚拟函数的覆盖。当基类Base的指针point指向派生类Derived的对象时,对point的print函数的调用实际上是调用了Derived的print函数而不是Base的print函数。这是面向对象中的多态性的体现。

//---------------------------------------------------------  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没有任何瓜葛,加不加都不影响重载的运作。

关于C++的隐藏规则(引用自《高质量C++/C 编程指南》林锐 2001):

(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual

关键字,基类的函数将被隐藏(注意别与重载混淆)。

(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual
关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。

这里,林锐博士好像犯了个错误。C++并没有隐藏规则,林锐博士所总结的隐藏规则是他错误地理解C++多态性所致。下面请看林锐博士给出的隐藏规则的例证:

#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 编程指南》是本很不错的书,可大家不要迷信哦。记住“只有在通过基类指针或引用间接指向派生类子类型时多态性才会起作用”。

纯虚函数:

C++语言为我们提供了一种语法结构,通过它可以指明,一个虚拟函数只是提供了一个可被子类型改写的接口。但是,它本身并不能通过虚拟机制被调用。这就是纯虚拟函数(pure virtual function)。 纯虚拟函数的声明如下所示:

class Query {  public:  // 声明纯虚拟函数  virtual ostream& print( ostream&=cout ) const = 0;  // ...  };

这里函数声明后面紧跟赋值0。

包含(或继承)一个或多个纯虚拟函数的类被编译器识别为抽象基类。试图创建一个抽象基类的独立类对象会导致编译时刻错误。(类似地通过虚拟机制调用纯虚拟函数也是错误的例如)

// Query 声明了纯虚拟函数  // 所以, 程序员不能创建独立的 Query 类对象  // ok: NameQuery 中的 Query 子对象  Query *pq = new NameQuery( "Nostromo" );  // 错误: new 表达式分配 Query 对象  Query *pq2 = new Query;

抽象基类只能作为子对象出现在后续的派生类中。

如果只知道virtual加在函数前,那对virtual只了解了一半,virtual还有一个重要用法是virtual public,就是虚拟继承。虚拟继承在C++ Primer中有详细的描述,下面稍作修改的阐释一下:

在缺省情况下C++中的继承是“按值组合”的一种特殊情况。当我们写

class Bear : public ZooAnimal { ... };

每个Bear 类对象都含有其ZooAnimal 基类子对象的所有非静态数据成员以及在Bear中声明的非静态数据成员类似地当派生类自己也作为一个基类对象时如:

class PolarBear : public Bear { ... };

则PolarBear 类对象含有在PolarBear 中声明的所有非静态数据成员以及其Bear 子对象的所有非静态数据成员和ZooAnimal 子对象的所有非静态数据成员。在单继承下这种由继承支持的特殊形式的按值组合提供了最有效的最紧凑的对象表示。在多继承下当一个基类在派生层次中出现多次时就会有问题最主要的实际例子是iostream 类层次结构。ostream 和istream 类都从抽象ios 基类派生而来,而iostream 类又是从ostream 和istream 派生

class iostream :public istream, public ostream { ... };

缺省情况下,每个iostream 类对象含有两个ios 子对象:在istream 子对象中的实例以及在ostream 子对象中的实例。这为什么不好?从效率上而言,存储ios 子对象的两个复本,浪费了存储区,因为iostream 只需要一个实例。而且,ios 构造函数被调用了两次每个子对象一次。更严重的问题是由于两个实例引起的二义性。例如,任何未限定修饰地访问ios 的成员都将导致编译时刻错误:到底访问哪个实例?如果ostream 和istream 对其ios 子对象的初始化稍稍不同,会怎样呢?怎样通过iostream 类保证这一对ios 值的一致性?在缺省的按值组合机制下,真的没有好办法可以保证这一点。

C++语言的解决方案是,提供另一种可替代按“引用组合”的继承机制虚拟继承(virtual inheritance )在虚拟继承下只有一个共享的基类子对象被继承而无论该基类在派生层次
中出现多少次共享的基类子对象被称为虚拟基类。

通过用关键字virtual 修政一个基类的声明可以将它指定为被虚拟派生。例如,下列声明使得ZooAnimal 成为Bear 和Raccoon 的虚拟基类:

// 关键字 public 和 virtual的顺序不重要  class Bear : public virtual ZooAnimal { ... };  class Raccoon : virtual public ZooAnimal { ... };

虚拟派生不是基类本身的一个显式特性,而是它与派生类的关系如前面所说明的,虚拟继承提供了“按引用组合”。也就是说,对于子对象及其非静态成员的访问是间接进行的。这使得在多继承情况下,把多个虚拟基类子对象组合成派生类中的一个共享实例,从而提供了必要的灵活性。同时,即使一个基类是虚拟的,我们仍然可以通过该基类类型的指针或引用,来操纵派生类的对象。

没有虚函数的C++不能面向对象。从商业的角度看,面向对象能使系统具有可扩展性和可适应性,但只有C++类的语法而没有面向对象的话,就不会减少维护成本,而实际上会增加成本。所以没有虚函数是万万不能的,而关键字virtual则是关键。

到此,关于“C++ Virtual是什么”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

C++ Virtual是什么

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

C++ Virtual是什么

这篇文章主要介绍“C++ Virtual是什么”,在日常操作中,相信很多人在C++ Virtual是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C++ Virtual是什么”的疑惑有所帮助!接下来,请跟
2023-06-17

C# 中virtual修饰符的作用是什么

C# 中virtual修饰符的作用是什么,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。C# virtual修饰符用于修改方法或属性的声明,在这种情况下,方法或属性被称作虚拟成员
2023-06-17

C#中Abstract 、Virtual和Override三者的区别是什么

C#中Abstract 、Virtual和Override三者的区别是什么?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。1. abstract 修饰符指示所修饰的内容缺少实现或
2023-06-15

C#中Abstract方法和Virtual方法有什么区别

本篇内容介绍了“C#中Abstract方法和Virtual方法有什么区别”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!简介:c#中Abstr
2023-06-30

C++报pure virtual function call错误怎么解决

C++中的"pure virtual function call"错误通常是由于在派生类中没有实现基类中的纯虚函数导致的。要解决这个错误,可以按照以下步骤进行操作:确保所有的纯虚函数都在派生类中进行了实现。纯虚函数是指在基类中被声明为纯虚函
C++报pure virtual function call错误怎么解决
2024-02-29

Vue中Virtual DOM和Diff原理及实现方法是什么

本篇内容介绍了“Vue中Virtual DOM和Diff原理及实现方法是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1. vdomvd
2023-07-05

Virtual Box虚拟机文件瘦身处理的方法是什么

本文小编为大家详细介绍“Virtual Box虚拟机文件瘦身处理的方法是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“Virtual Box虚拟机文件瘦身处理的方法是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习
2023-07-06

c++中_是什么

在 c++ 中,下划线 ('_') 用于:1. 匿名变量;2. 忽略参数;3. 宏定义(表示当前函数名);4. 保留关键字(覆盖 c++ 关键字);5. 转义字符(忽略分号);6. 预留标识符(标准库和用户库使用)。C++ 中的 _在 C
c++中_是什么
2024-04-26

c++中|是什么

c++ 中的 |(管道符)是位运算符,用于将两个整数的对应位按位或运算,即至少有一个非 0 则结果为 1。什么是 C++ 中的 |C++ 中的 |(管道符)是位运算符,用于将两个整数的对应位进行按位或运算。按位或运算按位或运算将两个二
c++中|是什么
2024-04-26

c#中///是什么

c# 中的多行注释以 /// 符号开头,可以跨越多行并记录代码元素的目的。这种注释用于记录目的、生成 xml 文档并增强代码的可读性和可维护性,同时还支持 visual studio 等工具中的 intellisense。///:C# 中的
c#中///是什么
2024-05-10

c#中:是什么

答案:c#中的 nameof 操作符返回指定表达式的名称。详细描述:nameof 操作符可用于以下类型表达式:字段属性方法事件类型类型成员(字段、属性、方法等)C#中的 nameof 操作符C#中的 nameof 操作符用于返回一个字符串
c#中:是什么
2024-05-10

C++ Test是什么

本篇内容介绍了“C++ Test是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!C++Test自动建立一个测试驱动程序,其设计目标是极大
2023-06-17

c++中什么是类,什么是对象

c++kquote>类在 c++ 中代表对象集合的模板,定义了对象的属性(数据成员)和行为(成员函数)。对象是类的实例,拥有类中的所有数据成员和成员函数,由类创建,并使用与类相同的数据类型。类和对象在 C++ 中的作用什么是类?类是
c++中什么是类,什么是对象
2024-05-08

c#什么是委托什么是事件

委托是一种指向方法的引用类型,用于实现松散耦合,而事件是一种特殊委托,用于事件处理。委托可将调用方法的职责转移到接收方,提高代码可重用性。事件允许对象向订阅者通知事件发生,订阅者可响应事件。使用委托需要定义一个与所调方法签名相同的委托类型,
c#什么是委托什么是事件
2024-04-04

c++里的?是什么

c++ 中的条件运算符 (? :) 是一个三元运算符,根据一个条件执行不同的操作。语法为:condition ? true_expression : false_expression,其中 condition 为布尔表达式,true_exp
c++里的?是什么
2024-04-22

C#替换是什么

这篇“C#替换是什么”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“C#替换是什么”文章吧。替换替换是替换模式中使用的正则表达
2023-06-17

什么是C#委托

这篇文章主要讲解了“什么是C#委托”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“什么是C#委托”吧!C#语言有很多值得学习的地方,这里我们主要介绍C#委托实例,包括介绍重复调用 Simple
2023-06-17

c++中cin是什么

cin 是 c++ 标准库中的输入流对象,用于从标准输入(键盘)读取数据,其语法为:std::cin >> variable; 可读取不同类型数据,如整数、浮点数或字符串。cin 将提取数据直至遇到空白字符或文件结束,并存储在指定的变量中。
c++中cin是什么
2024-04-26

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录