虚函数

抽象类中可以对虚函数进行实现,但子类必须要重新该函数

廉价感情. 提交于 2019-12-04 14:02:51
抽象类中可以对虚函数进行实现,但子类必须要重新该函数,否则子类仍然是一个抽象类,不能实例化。子类中调用基类的方法可以用基类名::方法.#include <iostream> using namespace std; class A { public: virtual void display()=0 { cout << "A" << endl; } }; class B :public A { public: virtual void display() { A::display(); cout << "B" << endl; } }; int main() { B b; b.display(); return 0; } 来源: https://www.cnblogs.com/Stephen-Qin/p/11869462.html

利用虚函数实现多态的方式:动态绑定

百般思念 提交于 2019-12-04 11:59:27
基类指针/引用调用虚成员函数时才会发生动态绑定,即只有在程序执行阶段才知道调用哪个版本的虚函数。此时调用对象为该指针/引用的动态类型对象。(静态类型就是定义时的类型,此处也就是基类的类型,一般静态和动态类型时相同的,只有在这种情况下两者可能不同。) OOP(面向对象编程)的核心思想“多态性”的根本就在于上述过程。 规避动态绑定 可以使用作用域运算符规避虚函数的动态绑定机制,这种情况通常是派生类虚函数要调用它覆盖的基类中的虚函数时。 派生类中的虚函数要覆盖基类虚函数时,形参必须严格匹配(不然就成了在派生类中定义一个重载函数)。按c++11标准,可以在想覆盖的派生类虚函数形参括号后加 override 关键字,让编译器帮忙检查。 禁止继承和覆盖 加 final 限定符可以禁止继承和覆盖 class A final{/* */}; // 类A无法被继承 如果一个类中的所有成员都是 public 的,可以用 struct 代替,两者的区别就在于成员的权限设置 struct B{ virtual void fun() final; //虚函数fun()无法被派生类覆盖 } struct C:B{} void fun(); //报错,无法覆盖 来源: https://www.cnblogs.com/j-c-y/p/11862603.html

第15章 面向对象程序设计 15.1 15.2

时光毁灭记忆、已成空白 提交于 2019-12-04 11:34:34
1 #include <iostream> 2 3 using namespace std; 4 5 class Quote{ 6 public: 7 string isbn() const; 8 virtual double net_price(size_t n) const; 9 10 }; 11 12 class Bulk_quote : public Quote{ 13 public: 14 double net_price(size_t) const override ; 15 16 }; 17 18 int main() { 19 20 } 1、 派生类必须在其内部对所有 重新定义 的虚函数进行声明。   派生类经常(但不总是)覆盖它继承的虚函数。如果派生类没有覆盖其基类中的某个虚函数,则该虚函数的行为类似于其它的普通成员,派生类会直接继承它在基类中的版本。   派生类可以在这样的函数之前加上virtual关键字,但是并不是非得这么做。C++11新标准允许派生类显式地注明它将使用哪个成员函数改写基类的虚函数,具体措施是在该函数的形参列表之后加一个 overrride 关键字。 2、 在C++语言中,当我们使用基类的引用或指针调用一个虚函数时将发生动态绑定。 1 Quote item; 2 Bulk_quote bulk; 3 Quote *p = &item; 4 p

【转】C#虚方法virtual详解

馋奶兔 提交于 2019-12-04 07:59:55
转: https://www.cnblogs.com/zhaoshujie/p/10502404.html 在C++、Java等众多OOP语言里都可以看到virtual的身影,而C#作为一个完全面向对象的语言当然也不例外。 虚拟函数从C#的程序编译的角度来看,它和其它一般的函数有什么区别呢?一般函数在编译时就静态地编译到了执行文件中,其相对地址在程序运行期间是不发生变化的,也就是写死了的!而虚函数在编译期间是不被静态编译的,它的相对地址是不确定的,它会根据运行时期对象实例来动态判断要调用的函数,其中那个申明时定义的类叫申明类,那个执行时实例化的类叫实例类。 如:飞禽 bird = new 麻雀(); 那么飞禽就是申明类,麻雀是实例类。 具体的检查的流程如下 : 1、当调用一个对象的函数时,系统会直接去检查这个对象申明定义的类,即申明类,看所调用的函数是否为虚函数; 2、如果不是虚函数,那么它就直接执行该函数。而如果有virtual关键字,也就是一个虚函数,那么这个时候它就不会立刻执行该函数了,而是转去检查对象的实例类。 3、在这个实例类里,他会检查这个实例类的定义中是否有重新实现该虚函数(通过override关键字),如果是有,那么OK,它就不会再找了,而马上执行该实例类中的这个重新实现的函数。而如果没有的话,系统就会不停地往上找实例类的父类,并对父类重复刚才在实例类里的检查

C++虚函数作用原理(一)——虚函数如何在C++语言逻辑中存在

て烟熏妆下的殇ゞ 提交于 2019-12-04 06:49:10
C++多态,接触其实也没太长的时间。上课的时候老师总是不停的讲,多态可以实现利用一个基类对象调用不同继承类的成员函数。我就会觉得很伤脑筋,这个的原理到底是什么?是什么呢? 开始的时候我觉得自己应该能够把它的原理想出来,因为一直觉得关于这些高级语言的事情,不论看起来多么神奇,其实应该都是之前学的基础累积起来的。所以,我就自己想了一段时间————能够实现这样效果的只有 **指针** 了。 但是这个指针应该怎么定义呢?就算基类中本身定义了一个指针,可是继承类中被继承来的函数在利用基类的时候怎么告诉基类到底调用哪一个继承类的函数呢?真的伤脑筋。 …… 现在是开始这个博客的第二天,我终于可以开始继续写了——因为我开始自己调试观察虚函数。 OK,现在开始了: 首先: 一、没有继承的类 (1)段小小的代码: class A { int x; int y; public: virtual void out() { cout << x << y << endl; cout<<"A"<<endl; } }; int main() { A a; return 0; } 十分明显这段程序中仅仅只有一个类,一个虚函数!现在我们一起来看看类A的成员到底如何分布的。 成员变量 x 和 y 存在。同时出现了 _vptr 。它是什么?没错儿就是虚函数表指针。也就是说这个_vptr就是指向虚函数表(或者理解它为指向

C++面向对象 继承与多态

不打扰是莪最后的温柔 提交于 2019-12-04 04:00:22
第十三章 多态与继承性 13.1继承与派生 1. 继承与派生的语法格式: Class 派生类名:继承方法 基类类名 { //新增的属性和行为 基类成员的覆盖或者重载 } 2. 继承方法有公开继承、保护继承和私有继承。三种继承方式分别用public, protected和private.无论哪种方式,派生类都全部继承了基类的一切成员(基类的构造函数、拷贝构造函数和析构函数除外) 3. 派生类型的访问属性:基类的私有成员在派生类当中是存在的,但是被隔离起来,不能直接访问。如果要访问他们需要通过基类的成员函数。 4. 派生类的对象可以初始化(拷贝构造)基类的对象、初始化基类的引用、赋值给基类的对象。派生类对象的地址可以初始化基类的指针变量,可以赋值给基类的指针变量。 5. 虚函数:使用保留字virtual可以将成员函数声明为虚函数。 6. 友元函数不能是虚函数! 7. 可能作为基类的类应该定义他的析构函数为虚函数(比较好) 8. 纯虚函数(在函数首部之后,写下记号“=0”之后虚函数就是纯虚函数) 9. 只要一个类含有纯虚函数,那这个类就是抽象类。抽象类的唯一作用就是被继承。派生类当中可以覆盖定义基类的纯虚函数。抽象类的派生类也可能是抽象类,只要它当中还有纯虚函数没有被覆盖定义。当一个类中没有纯虚函数的时候,这个类便成为具体类。 10. 不能创建抽象类的对象

c++虚函数的原理及实现

梦想的初衷 提交于 2019-12-03 22:09:02
虚函数是在类中被声明为virtual的成员函数,当编译器看到通过指针或引用调用此类函数时,对其执行晚绑定,即通过指针(或引用)指向的类的类型信息来决定该函数是哪个类的。通常此类指针或引用都声明为基类的,它可以指向基类或派生类的对象。 多态指同一个方法根据其所属的不同对象可以有不同的行为(根据自己理解,不知这么说是否严谨)。 举个例子说明虚函数、多态、早绑定和晚绑定: 李氏两兄妹(哥哥和妹妹)参加姓氏运动会(不同姓氏组队参加),哥哥男子项目比赛,妹妹参加女子项目比赛,开幕式有一个参赛队伍代表发言仪式,兄妹俩都想 去露露脸,可只能一人去,最终他们决定到时抓阄决定,而组委会也不反对,它才不关心是哥哥还是妹妹来发言,只要派一个姓李的来说两句话就行。运动会如期举 行,妹妹抓阄获得代表李家发言的机会,哥哥参加了男子项目比赛,妹妹参加了女子项目比赛。比赛结果就不是我们关心的了。 现在让我们来做个类比(只讨论与运动会相关的话题): (1)类的设计: 李氏兄妹属于李氏家族,李氏是基类(这里还是抽象的纯基类),李氏又派生出两个子类(李氏男和李氏女),李氏男会所有男子项目的比赛(李氏男的成员函 数),李氏女会所有女子项目的比赛(李氏女的成员函数)。姓李的人都会发言(基类虚函数),李氏男和李氏女继承自李氏当然也会发言,只是男女说话声音不一 样,内容也会又差异,给人感觉不同

C++中虚函数实现原理揭秘

不羁岁月 提交于 2019-12-03 22:08:47
编译器到底做了什么实现的虚函数的晚绑定呢?我们来探个究竟。 编译器对每个包含虚函数的类创建一个表(称为V TA B L E)。在V TA B LE中,编译器放置特定类的虚函数地址。在每个带有虚函数的类 中,编译器秘密地置一指针,称为v p o i n t e r(缩写为V PT R),指向这个对象的V TA B L E。通过基类指针做虚函数调用时(也就是做多态调用时),编译器静态地插入取得这个V P TR,并在V TA B L E表中查找函数地址的代码,这样就能调用正确的函数使晚捆绑发生。为每个类设置V TA B L E、初始化V PTR、为虚函数调用插入代码,所有这些都是自动发生的,所以我们不必担心这些。利用虚函数,这个对象的合适的函数就能被调用,哪怕在编译器还不知道这个对象的特定类型的情况下。(《C++编程思想》) 在任何类中不存在显示的类型信息,可对象中必须存放类信息,否则类型不可能在运行时建立。那这个类信息是什么呢?我们来看下面几个类: class no_virtual { public: void fun1() const{} int fun2() const { return a; } private: int a; } class one_virtual { public: virtual void fun1() const{} int fun2() const {

c++晚捆绑的实现机制

Deadly 提交于 2019-12-03 22:07:46
早绑定 (early binding)是指在实例化对象之前定义它的属性和方法,这样编译器或解释程序就能够提前转换机器代码。 晚绑定 (late binding)指的是编译器或解释程序在运行前,不知道对象的类型。使用晚绑定,无需检查对象的类型,只需检查对象是否支持属性和方法即可。 早绑定的优点是: (1) 编译效率 高 (2) 有代码提示 (3) 编译时类型检查 晚绑定的优点是: (1) 不用申明类型 (2) 对象类型可以随时更改 virtual关键字可以告诉编译器实行的是晚捆绑(虚函数)。 为了实现晚捆绑,典型的编译器对每个包含虚函数的类将创建一个表(VTABLE),在VTABLE中放着特定类的虚函数地址。在每个带有虚函数的类中,编译器会放置一个指针VPTR,指向这个对象的VTABLE。当通过基类指针做虚函数调用时,编译器静态的插入能取得这个VPTR并在VTAVLE表中查找函数地址的代码,这样就会引起晚捆绑的发生。 #define _CRT_SECURE_NO_WARNINGS #include<iostream> using namespace std; class NoVirtual { public: void fun() {} int ret() {} private: int a; }; class OneVirtual { public: virtual void

动态多态的原理

时间秒杀一切 提交于 2019-12-03 22:07:31
多态的三个条件:1.继承,2.虚函数重写,3.父类指针或引用指向子类对象 什么是多态?相同对象收到不同消息或不同对象收到相同消息时产生的不同动作。 首先是多态的分类,分为静态多态和动态多态,也可以称为早绑定和晚绑定,区分的时刻就是程序在编译阶段根据参数个数确定调用哪个函数,如果能够确定就是早绑定如果不能确定就是晚绑定,如果要实现多态就必须要使用虚函数。 从编译角度看: c++编译器在编译的时候,要确定每个对象调用的函数(非虚函数)的地址称为早期绑定,当我们将子类的对象son的地址赋给父类变量时,c++编译器进行了类型转换,此时c++编译器认为父类变量保存的就是父类对象的地址,当在main函数中执行父类变量的方法时,调用的当然就是父类对象的函数。 从内存角度看: 我们构造子类的对象时,首先要调用父类的构造函数去构造父类的对象,然后才调用子类的构造函数完成自身部分的构造,从而拼接出一个完整的子类对象。当我们将子类对象转换为父类型时,该对象就被认为是原对象整个内存模型的上半部分,那么当利用类型转换后的对象指针去调用它的方法时,当然也就是调用它所在的内存中的方法。   正如很多人那么认为,父类变量指向的是子类的对象,如果希望输出的是子类的方法,就要用到虚函数了。   前面输出的结果是因为编译器在编译的时候,就已经确定了对象调用的函数的地址,要解决这个问题就要使用晚绑定