浏览量 1 1.类里如果声明了虚函数,这个函数是实现的,哪怕是空实现,它的作用就是为了能让这个函数在它的子类里面可以被覆盖(override),这样的话,编译器就可以使用后期绑定来达到多态了。 纯虚函数只是一个接口,是个函数的声明而已,它要留到子类里去实现。 2.虚函数在子类里面可以不重写;但纯虚函数必须在子类实现才可以实例化子类。 3.虚函数的类用于 “实作继承”,继承接口的同时也继承了父类的实现。纯虚函数关注的是接口的统一性,实现由子类完成。 4.带纯虚函数的类叫抽象类,这种类不能直接生成对象,而只有被继承,并重写其虚函数后,才能使用。抽象类被继承后,子类可以继续是抽象类,也可以是普通类。 5.虚基类是虚继承中的基类,具体见下文虚继承。
虚函数 代码如下定义: // test1107.cpp : 定义控制台应用程序的入口点。 son s; cout<<f.get_age()<<endl; cout<<s.get_age()<<endl; system("pause"); } 输出为: 1 0 在基类中的虚函数 当基类中的虚函数定义时,是使用指针或者引用作为参数,那么在运行是,要判断传入的参数,是基类的对象,还是派生类的对象。 如果是基类的对象,则调用基类中的虚函数定义。 如果是派生类的对象,则调用派生类中对基类虚函数的新定义的函数。
虚函数(impure virtual) C++的虚函数主要作用是“运行时多态”,父类中提供虚函数的实现,为子类提供默认的函数实现。 子类可以重写父类的虚函数实现子类的特殊化。 ; 纯虚函数(pure virtual) C++中包含纯虚函数的类,被称为是“抽象类”。 C++中的纯虚函数也是一种“运行时多态”。 } //虚函数 virtual void xhs(){ //这个虚函数必须得在基类中实现 cout<<"我是基类的虚函数"<<endl;//即使是空的虚函数也要在基类中实现 } //派生类中可以不写这个函数,但是派生类对象调用时会调用积累的虚函数 //纯虚函数 virtual void cxhs() =0; //这个纯虚函数不在基类中实现,必须在子类中实现
,在基类的类定义中定义虚函数的一般形式: virtual 函数返回值类型 虚函数名(形参表) { 函数体 } 虚函数的作用是实现动态联编,也就是在程序的运行阶段动态地选择合适的成员函数,在定义了虚函数后 , 可以在基类的派生类中对虚函数重新定义(形式也是:virtual 函数返回值类型 虚函数名(形参表){ 函数体 }),在派生类中重新定义的函数应与虚函数具有相同的形参个数和形参类型。 定义虚函数的限制: (1)非类的成员函数不能定义为虚函数,类的成员函数中静态成员函数和构造函数也不能定义为虚函数,但可以将析构函数定义为虚函数。 :0012FED4 虚函数表 — 第一个函数地址:0044F148 Base::f 通过这个示例,我们可以看到,我们可以通过强行把&b转成int *,取得虚函数表的地址,然后,再次取址就可以得到第一个虚函数的地址了 我们可以看到下面几点: 1)虚函数按照其声明顺序放于表中。 2)父类的虚函数在子类的虚函数前面。 一般继承(有虚函数覆盖) 覆盖父类的虚函数是很显然的事情,不然,虚函数就变得毫无意义。
2.1.3虚函数的重写/覆盖 虚函数的重写/覆盖:派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型、函数名字、参数类型完全相同),称派生类的虚函数重写了基类的虚函数。 无法实例化处对象就意味着很多功能就无法实现,所以如果父类中有纯虚函数,子类就要重写纯虚函数。 4.多态的原理 4.1虚函数表指针 大家可以思考一下在32位下b是多大。 而指针我们都知道,再32位下是4个字节,64位下是8个字节,我在测试的时候是在32位环境下,所以_vfptr,int和char三个加起来,根据对其原则,最后得出是12。 4.派生类的虚函数表中包含,(1)基类的虚函数地址,(2)派生类重写的虚函数地址完成覆盖,派生类自己的虚函数地址三个部分。 因为int取4个字节,我们对int*解引用就可以拿到前四个字节。 这里就拿到了虚函数表的地址,我么通过观察可以看出和常量区的地址最为接近,所以在vs下,虚函数表就存在常量区。
前四个字节存储的是虚函数表的指针vfptr,后四个字节存储对象成员var的值。虚函数表的大小为4字节,就一条函数地址,即虚函数fun的地址,它在虚函数表vftable的偏移是0。 图1 MyClass对象模型 MyClass的虚函数表虽然只有一条函数记录,但是它的结尾处是由4字节的0作为结束标记的。 4 0 虚继承的引入把对象的模型变得十分复杂,除了每个基类(MyClassA和MyClassB)和公共基类(MyClass)的虚函数表指针需要记录外,每个虚拟继承了MyClass的父类还需要记录一个虚基类表 MyClassC的对象模型如图4所示。 ? 图4 MyClassC对象模型 虚基类表每项记录了被继承的虚基类子对象相对于虚基类表指针的偏移量。 和虚函数表不同的是,虚基类表的第一项记录着当前子对象相对与虚基类表指针的偏移。MyClassA和MyClassB子对象内的虚表指针都是存储在相对于自身的4字节偏移处,因此该值是-4。
文章目录 引言: 一、虚函数的定义和使用 关于虚函数,说明以下几点: 虚函数的访问 二、纯虚函数 三、补充内容 指向类中数据成员的指针变量 例题: 引言: 若要访问派生类中相同名字的函数,必须将基类中的同名函数定义为 3、虚函数必须是类的一个成员函数,不能是友元函数,也不能是静态 的成员函数。 4、在派生类中没有重新定义虚函数时,与一般的成员函数一样,当调 用这种派生类对象的虚函数时,则调用其基类中的虚函数。 当用这种基类 指针指向其派生类的对象时,必须在派生类中重载纯虚函数,否则会 产生程序的运行错误。 4、在以抽象类作为基类的派生类中必须有纯虚函数的实现部分,即必 须有重载纯虚函数的函数体。 PointName 4、由于这种指针变量并不是类的成员,所以使用它只能访问对象的公有数据成员。若要访问对象的 私有数据成员,必须通过成员函数来实现。 3、使用这种指针变量来调用成员函数时,必须指明调用那一个对象的成员函数, 这种指针变量是不能单独使用的。用对象名引用。 4、由于这种指针变量不是类的成员,所以用它只能调用公有的成员函数。
C++的虚函数是一种特殊的成员函数,用于实现多态性。虚函数允许在基类中声明一个函数,在派生类中根据需要进行重写,并通过基类指针或引用来调用派生类对象的特定实现。 ①虚函数的声明 在基类中,我们可以使用关键字virtual来声明一个虚函数。 ptr->show(); ③派生类重写虚函数 派生类可以重写基类中的虚函数,以提供自己的实现。 ." << endl; } }; ④纯虚函数 虚函数也可以被声明为纯虚函数,即没有默认实现的虚函数。纯虚函数通过在声明中使用= 0来标识。 虚函数使用动态绑定,即运行时将根据对象的实际类型选择正确的函数实现。 构造函数不能是虚函数。 静态成员函数不能是虚函数。 虚函数可以被继承,派生类可以选择是否重写虚函数。
对于经常被问到的虚函数和多态的问题,发现百度百科回答得十分详细,所以自己在百度百科上的解释进行总结 一、虚函数 (1)虚函数简介:在某基类中声明为virtual并在一个或者多个派生类中被重新定义的成员函数 (4)过程说明: 先给定代码,对以下代码做说明: 1 #include<iostream> 2 using namespace std; 3 class Animal 4 { 5 public 所以我们对main( )函数做修改,采用指向基类的指针或引用来调用: 1 int main() 2 { 3 Animal animal; 4 Dog dog; 5 (5)限制条件: 非类的成员函数不能定义为虚函数,类的成员函数中静态函数、构造函数也不能定义为虚函数,但是析构函数可以被定义为虚函数; 当基类中的某一成员函数声明为虚函数后,派生类中的同名函数(函数名相同 、参数列表完全一致、返回类型相关)自动成为虚函数; 如果声明了某个成员函数为虚函数,则在该类中不能出现和这个成员函数同名并且返回值、参数个数、类型都相同的非虚函数;在以该类为基类的派生类中,也不能出现这种同名函数
(有虚函数覆盖) 4、多重继承情况(无虚函数覆盖) 5、多重继承情况(有虚函数覆盖) 四、虚函数的相关问题 1、构造函数为什么不能定义为虚函数 2、析构函数为什么要定义为虚函数? 只要有虚函数,C++类都会存在这样的一张虚函数表,不管是普通虚函数亦或是纯虚函数,亦或是派生类中隐式声明的这些虚函数都会生成这张虚函数表。 虚函数表创建的时间:在一个类构造的时候,创建这张虚函数表,而这个虚函数表是供整个类所共有的。虚函数表存储在对象最开始的位置。虚函数表其实就是函数指针的地址。 ; cout<<(Fun)(*((int*)*(int*)b+4))<<endl; //最后一个位置为0 表明虚函数表结束 +4是因为定义了一个 虚析构函数 delete b; 4、多重继承情况(无虚函数覆盖) 下面,再让我们来看看多重继承中的情况,假设有下面这样一个类的继承关系。注意:子类并没有覆盖父类的函数。
定义 纯虚函数就是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。 纯虚函数的意义在于,让所有的类对象(主要是派生类对象)都可以执行纯虚函数的动作,但类无法为纯虚函数提供一个合理的缺省实现。 纯虚函数和虚函数有什么区别 纯虚函数声明如下:virtual void function1()=0; 纯虚函数一定没有定义,纯虚函数用来规范派生类的行为, 即接口。 实现了纯虚函数的子类,该纯虚函数在子类中就变成了了虚函数,子类的子类可以覆盖该虚函数,由多态方式调用的时候动态绑定。 虚函数是C++中用于实现多态的机制。核心理念就是通过基类访问派生类定义的函数。 在有动态分配堆上内存的时候,析构函数必须是虚函数,但没有必要是纯虚的。 友元不是成员函数,只有成员函数才可以使虚拟的,因此友元不能是虚拟函数。
说明: 1.虚函数列表中的最后一个.表示的是虚函数列表的结束符,类似于字符串的/0。 2.虚函数指针往往是在类对象的第一个元素。 3.对于派生类而言,如果派生类实现了基类中的虚函数,在派生类的虚函数列表中,对应的虚函数会被替换成派生类的这个函数地址。 对象b对应的虚指针b:虚函数表的第0个函数地址:0x400f10 // 这里是对象b对应的虚函数列表首地址Base f() .b:虚函数表的第1个函数地址:0x400f18Base print() .b1 对象b1对应的虚指针b1:虚函数表的第0个函数地址:0x400f10 // 这里是对象b1对应的虚函数列表首地址Base f() .b1:虚函数表的第1个函数地址:0x400f18Base print( 基类的虚函数列表和继承类的虚函数列表是两个,是不相同的,继承类的虚函数列表中存储的是继承类的虚函数实现,如果继承类没有实现基类的虚函数的话,会存储基类的虚函数地址。例子参见继承类的执行结果。
言归正传,让我们一起进入虚函数的世界。 虚函数表 对C++ 了解的人都应该知道虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的。简称为V-Table。 Fun)*((int*)*(int*)(&b)); pFun(); 实际运行经果如下:(Windows XP+VS2003, Linux 2.6.22 + GCC 4.1.3) 虚函数表地址:0012FED4 那么,在派生类的实例中,其虚函数表如下所示: 对于实例:Derive d; 的虚函数表如下: 我们可以看到下面几点: 1)虚函数按照其声明顺序放于表中。 2)父类的虚函数在子类的虚函数前面。 一般继承(有虚函数覆盖) 覆盖父类的虚函数是很显然的事情,不然,虚函数就变得毫无意义。下面,我们来看一下,如果子类中有虚函数重载了父类的虚函数,会是一个什么样子? int*)((int*)&d+0)+3); pFun = (Fun)pVtab[0][3]; pFun(); //The tail of the vtable pFun = (Fun)pVtab[0][4]
必须是虚函数(派生类一定要重写基类中的虚函数) ---- Q2:什么是纯虚函数,与虚函数的区别 1、定义一个函数为虚函数,不代表函数为不被实现的函数。 所以,用户不能创建类的实例,只能创建它的派生类的实例,它必须在继承类中重新声明函数。 4、定义纯虚函数的目的在于,使派生类仅仅只是继承函数的接口。 endl; } }; int main() { A* a = new B(); B* b = new B(); //a->show(); //b->v(); } A B A B ---- Q4: 3、虚函数表存储虚函数的地址,即虚函数表的元素是指向类成员函数的指针,而类中虚函数的个数在编译时期可以确定,即虚函数表的大小可以确定,即大小是在编译时期确定的,不必动态分配内存空间存储虚函数表。 4 个字节,这 4 个字节就是实现多态的关键——它位于对象存储空间的最前端,其中存放的是虚函数表的地址。
虚函数的使用方法(以下内容 摘自《C++面向对象程序》): (1)在基类用virtual声明成员函数为虚函数。 C++规定,当一个成员函数被声明为虚函数后,其派生类中的同名函数都自动成为虚函数。 (4)通过该指针变量调用此虚函数,此时调用的就是指针变量指向的对象的同名函数。 什么时候应该把一个成员函数声明为虚函数呢? (1)首先看成员函数所在的类是否会作为基类。然后看成员函数在类的继承后有无可能被更改功能,如果希望更改其功能的,一般应该讲其声明为虚函数。 (3)应该考虑成员函数的调用是通过对象名还是通过基类指针或引用去访问,如果是通过基类指针或引用去访问,则应当什么为虚函数。 (4)有时在定义虚函数时,并不定义其函数体,即函数体是空的。
关键词:虚函数,虚表,虚表指针,动态绑定,多态 一、概述 为了实现C++的多态,C++使用了一种动态绑定的技术。这个技术的核心是虚函数表(下文简称虚表)。本文介绍虚函数表是如何实现动态绑定的。 所以如果一个基类包含了虚函数,那么其继承类也可调用这些虚函数,换句话说,一个类继承了包含虚函数的基类,那么这个类也拥有自己的虚表。 我们来看以下的代码。 类A包含虚函数vfunc1,vfunc2,由于类A包含虚函数,故类A拥有一个虚表。 图1:类A的虚表示意图 虚表是一个指针数组,其元素是虚函数的指针,每个元素对应一个虚函数的函数指针。 C: public B { public: virtual void vfunc2(); void func2(); private: int m_data1, m_data4;
一、虚存的引入(为什么?) 突破了程序常驻内存。 二、覆盖与交换技术 0.简介 虚存是在覆盖与交换技术上发展而来。 分区式内存管理方式,对作业的大小有严格的限制,作业运行时,系统将作业的全部信息一次装入内存,并一直驻留内存,直至运行结束。 覆盖和交换技术是实现虚存管理的最基本的方法,让几个程序段共享一段内存空间,通过覆盖和交换,把暂时不需要运行的程序段调出内存,腾出内存空间,把将要运行的程序段调入内存执行。 五、虚存的实现机制(重要) 1.缺页中断 中断是指计算机在执行程序的过程中,当出现异常情况或特殊请求时,计算机停止现行程序的运行,转向对这些异常情况或特殊请求的处理,处理结束后再返回现行程序的间断处, 在图6-4中示出了一个例子。如在执行一条指令COPY A TO B时,可能要产生6次缺页中断,其中指令本身跨了两个页面,A和B又分别各是一个数据块,也都跨了两个页面。
这一篇文章来讲讲C++的多态、虚函数、纯虚函数。 C++多态 多态:C++中的多态分为静态多态,动态多态。 : 您可能想要在基类中定义虚函数,以便在派生类中重新定义该函数更好地适用于对象,但是您在基类中又不能对虚函数给出有意义的实现,这个时候就会用到纯虚函数。 纯虚函数就是虚函数的函数主体=0,也就是没有主体。 { width = a; height = b; } virtual int area() = 0; } 注意: ①虚函数和纯虚函数在多态中,存在差异,虚函数在派生类中可以选择是否重写该虚函数 而纯虚函数必须在派生类中实现该纯虚函数。 ②当类中存在纯虚函数,则该类为抽象类。
首先理解一下分开的意思 成员函数后面用 const 修饰,const表示this是一个指向常量的指针,即对象成为一个常量,即它的成员不能够变化. 例如在Sales_data成员函数中,this的类型是Sales_data *const,即类一旦实例化一个对象后,this指向这个对象,是不能改变的,但是对象本身可以变) =0表示这个成员函数是纯虚函数 ,也就是它可以没有定义,只有接口,由它的继承类具体定义它的行为,当然,你也可以给它定义缺省的函数体 一个类里如果包含 =0 的纯虚函数,那么这个类就是一个抽象类,它不能具体实例化(不能创建它的对象), 而只能由它去派生子类 合起来在虚函数后面–>纯虚函数 const 写在函数后头还=0这里不是const=0,虚函数表示方法是 virtual 返回值 函数名(参数表){函数体} , 在继承的时候可以在子类中从新定义这个函数 如果你的子类中都重新定义了这个函数,那个父类中函数的定义就没有什么用了所以可以不定义只说明就行,也就是定义为纯虚函数形如: virtual 返回值 函数名(参数表)=0;这里就不用定义实际的函数了。
本文将深入解析虚函数与纯虚函数的区别,并通过实例展示它们在实际编程中的应用。一、虚函数虚函数是指在C++中,被virtual关键字修饰的成员函数。 虚函数表保存了类中所有虚函数的地址,通过虚表指针可以找到对应的函数地址,从而实现动态绑定。虚函数可以有实现,也可以没有实现。在子类中,虚函数可以被覆盖,也可以不被覆盖。 如果子类没有覆盖基类的虚函数,那么当通过基类指针或引用调用该函数时,将调用基类的虚函数实现。二、纯虚函数纯虚函数是一种特殊的虚函数,它在声明时除了加上virtual关键字外,还需要加上=0。 三、虚函数与纯虚函数的区别定义方式:虚函数在定义时在普通函数的基础上加上virtual关键字,而纯虚函数在定义时除了加上virtual关键字外,还需要加上=0。 多态性:虚函数和纯虚函数都可以实现多态性,但纯虚函数更多地用于定义抽象接口,而虚函数则用于实现具体的多态行为。类类型:包含虚函数的类可以是普通类,也可以是抽象类;而包含纯虚函数的类一定是抽象类。