浏览量 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; //这个纯虚函数不在基类中实现,必须在子类中实现
3、 必须先使用基类指针指向子类型的对象,然后直接或者间接使用基类指针调用虚函数。 (3)如果声明了某个成员函数为虚函数,则在该类中不能出现和这个成员函数同名并且返回值、参数个数、参数类型都相同的非虚函数。 如: Derive d; Base1 *b1 = &d; Base2 *b2 = &d; Base3 *b3 = &d; b1->f(); //Derive ::f() b2->f(); //Derive::f() b3->f(); //Derive::f() b1->g(); //Base1::g() b2->g() ; //Base2::g() b3->g(); //Base3::g() 安全性 每次写C++的文章,总免不了要批判一下C++。
2.1.3虚函数的重写/覆盖 虚函数的重写/覆盖:派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型、函数名字、参数类型完全相同),称派生类的虚函数重写了基类的虚函数。 3.纯虚函数和抽象类 在虚函数的后面写上=0,则这个函数为纯虚函数,纯虚函数不需要定义实现(实现没啥意义因为要被派生类重写,但是语法上可以实现),只要声明即可。 用图来表示就如上图所示,虚函数表这里先简单提一下,下面会详细讲。 虚函数表又叫虚函数指针数组或者虚表,里面存的就是虚函数的指针。 3.派生类中重写的基类的虚函数,派生类的虚函数表中对应的虚函数就会被覆盖成派生类重写的虚函数地址。 4.派生类的虚函数表中包含,(1)基类的虚函数地址,(2)派生类重写的虚函数地址完成覆盖,派生类自己的虚函数地址三个部分。
前四个字节存储的是虚函数表的指针vfptr,后四个字节存储对象成员var的值。虚函数表的大小为4字节,就一条函数地址,即虚函数fun的地址,它在虚函数表vftable的偏移是0。 并且虚函数表内的记录多了一条——MyClassA自己定义的虚函数funA。它的对象模型如图2所示。 ? 如果子类定义了新的虚函数,虚函数表内会追加一条记录,记录该函数的地址(如MyClassA::funA)。 使用这种方式,就可以实现多态的特性。 而且每一个父类都对应一个单独的虚函数表。MyClassC的对象模型如图3所示。 ? 图3 MyClassC对象模型 多重继承下,子类不再具有自身的虚函数表,它的虚函数表与第一个父类的虚函数表合并了。 同样的,如果子类重写了任意父类的虚函数,都会覆盖对应的函数地址记录。如果MyClassC重写了fun函数(两个父类都有该函数),那么两个虚函数表的记录都需要被覆盖!
文章目录 引言: 一、虚函数的定义和使用 关于虚函数,说明以下几点: 虚函数的访问 二、纯虚函数 三、补充内容 指向类中数据成员的指针变量 例题: 引言: 若要访问派生类中相同名字的函数,必须将基类中的同名函数定义为 3、虚函数必须是类的一个成员函数,不能是友元函数,也不能是静态 的成员函数。 4、在派生类中没有重新定义虚函数时,与一般的成员函数一样,当调 用这种派生类对象的虚函数时,则调用其基类中的虚函数。 3、把至少包含一个纯虚函数的类,称为抽象类。这种类只能作为派生 类的基类,不能用来创建对象。 其理由是明显的:因为虚函数没有实现部分,所以不能产生对象。 3、用这种指针访问数据成员时,必须指明是使用那一个对象的数据成员。当与对象结合使用时, 其用法为: ObjectName. 3、使用这种指针变量来调用成员函数时,必须指明调用那一个对象的成员函数, 这种指针变量是不能单独使用的。用对象名引用。 4、由于这种指针变量不是类的成员,所以用它只能调用公有的成员函数。
C++的虚函数是一种特殊的成员函数,用于实现多态性。虚函数允许在基类中声明一个函数,在派生类中根据需要进行重写,并通过基类指针或引用来调用派生类对象的特定实现。 ①虚函数的声明 在基类中,我们可以使用关键字virtual来声明一个虚函数。 ptr->show(); ③派生类重写虚函数 派生类可以重写基类中的虚函数,以提供自己的实现。 ." << endl; } }; ④纯虚函数 虚函数也可以被声明为纯虚函数,即没有默认实现的虚函数。纯虚函数通过在声明中使用= 0来标识。 虚函数使用动态绑定,即运行时将根据对象的实际类型选择正确的函数实现。 构造函数不能是虚函数。 静态成员函数不能是虚函数。 虚函数可以被继承,派生类可以选择是否重写虚函数。
对于经常被问到的虚函数和多态的问题,发现百度百科回答得十分详细,所以自己在百度百科上的解释进行总结 一、虚函数 (1)虚函数简介:在某基类中声明为virtual并在一个或者多个派生类中被重新定义的成员函数 (2)简单解释:被virtual关键字修饰的成员函数,就是虚函数。 (3)作用:实现多态性(polymorphism)。 所以我们对main( )函数做修改,采用指向基类的指针或引用来调用: 1 int main() 2 { 3 Animal animal; 4 Dog dog; 5 (5)限制条件: 非类的成员函数不能定义为虚函数,类的成员函数中静态函数、构造函数也不能定义为虚函数,但是析构函数可以被定义为虚函数; 当基类中的某一成员函数声明为虚函数后,派生类中的同名函数(函数名相同 、参数列表完全一致、返回类型相关)自动成为虚函数; 如果声明了某个成员函数为虚函数,则在该类中不能出现和这个成员函数同名并且返回值、参数个数、类型都相同的非虚函数;在以该类为基类的派生类中,也不能出现这种同名函数
文章目录 一、多态与重载 1、多态的概念 2、重载—编译期多态的体现 3、虚函数—运行期多态的体现 二、虚函数实例 三、虚函数的实现(内存布局) 1、无继承情况 2、单继承情况(无虚函数覆盖) 3、单继承情况 3、如何去验证虚函数表的存在 一、多态与重载 1、多态的概念 面向对象的语言有三大特性:继承、封装、多态。虚函数作为多态的实现方式,重要性毋庸置疑。 3、虚函数—运行期多态的体现 运行期多态发生的三个条件:继承关系、虚函数覆盖、父类指针或引用指向子类对象。 3、单继承情况(有虚函数覆盖) 覆盖父类的虚函数是很显然的事情,不然,虚函数就变得毫无意义。下面,我们来看一下,如果子类中有虚函数重载了父类的虚函数,会是一个什么样子? ::f() b3->f(); //Derive::f() b1->g(); //Base1::g() b2->g(); //Base2::g() b3->g(); //Base3::g() 四、虚函数的相关问题
定义 纯虚函数就是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。 纯虚函数的意义在于,让所有的类对象(主要是派生类对象)都可以执行纯虚函数的动作,但类无法为纯虚函数提供一个合理的缺省实现。 纯虚函数和虚函数有什么区别 纯虚函数声明如下:virtual void function1()=0; 纯虚函数一定没有定义,纯虚函数用来规范派生类的行为, 即接口。 实现了纯虚函数的子类,该纯虚函数在子类中就变成了了虚函数,子类的子类可以覆盖该虚函数,由多态方式调用的时候动态绑定。 虚函数是C++中用于实现多态的机制。核心理念就是通过基类访问派生类定义的函数。 在有动态分配堆上内存的时候,析构函数必须是虚函数,但没有必要是纯虚的。 友元不是成员函数,只有成员函数才可以使虚拟的,因此友元不能是虚拟函数。
3.对于派生类而言,如果派生类实现了基类中的虚函数,在派生类的虚函数列表中,对应的虚函数会被替换成派生类的这个函数地址。 ) .d:虚函数表的地址:0x7fffd27d5e90 // 3. 虚函数列表跟类是绑定的,每一个类会生成一个虚函数列表的地址,应该是存储在全局数据区。 3. 基类的虚函数列表和继承类的虚函数列表是两个,是不相同的,继承类的虚函数列表中存储的是继承类的虚函数实现,如果继承类没有实现基类的虚函数的话,会存储基类的虚函数地址。例子参见继承类的执行结果。 () .d:虚函数表的第[0][1]个函数地址:0x400e98Base print() .d:虚函数表的第[1][0]个函数地址:0x400eb8 // 3.第二个基类的虚函数首地址Derive f1
那么,在派生类的实例中,其虚函数表如下所示: 对于实例:Derive d; 的虚函数表如下: 我们可以看到下面几点: 1)虚函数按照其声明顺序放于表中。 2)父类的虚函数在子类的虚函数前面。 一般继承(有虚函数覆盖) 覆盖父类的虚函数是很显然的事情,不然,虚函数就变得毫无意义。下面,我们来看一下,如果子类中有虚函数重载了父类的虚函数,会是一个什么样子? 如: Derive d; Base1 *b1 = &d; Base2 *b2 = &d; Base3 *b3 = &d; b1->f(); //Derive::f() b2->f(); //Derive ::f() b3->f(); //Derive::f() b1->g(); //Base1::g() b2->g(); //Base2::g() b3->g(); //Base3::g() 安全性 每次写 { public: virtual void f() { cout << “Base3::f” << endl; } virtual void g() { cout << “Base3::g” <<
必须是虚函数(派生类一定要重写基类中的虚函数) ---- Q2:什么是纯虚函数,与虚函数的区别 1、定义一个函数为虚函数,不代表函数为不被实现的函数。 2、纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加"=0" 3、声明了纯虚函数的类是一个抽象类。 (这句话刚开始还真没反应过来,也是啊,基类都不能初始化对象了,还怎么去调用基类方法啊) ---- Q3:抽象基类派生类对象可以调用基类方法吗? 3、虚函数表存储虚函数的地址,即虚函数表的元素是指向类成员函数的指针,而类中虚函数的个数在编译时期可以确定,即虚函数表的大小可以确定,即大小是在编译时期确定的,不必动态分配内存空间存储虚函数表。 多态的函数调用语句被编译成根据基类指针所指向的(或基类引用所引用的)对象中存放的虚函数表的地址,在虚函数表中查找虚函数地址,并调用虚函数的一系列指令。
虚函数的使用方法(以下内容 摘自《C++面向对象程序》): (1)在基类用virtual声明成员函数为虚函数。 C++规定,当一个成员函数被声明为虚函数后,其派生类中的同名函数都自动成为虚函数。 如果在派生类中没有对基类的虚函数重新定义,派生类简单地继承其直接基类的虚函数。 (3)定义一个指向基类对象的指针变量,并使它指向同一类族中需要调用该函数的对象。 什么时候应该把一个成员函数声明为虚函数呢? (1)首先看成员函数所在的类是否会作为基类。然后看成员函数在类的继承后有无可能被更改功能,如果希望更改其功能的,一般应该讲其声明为虚函数。 (3)应该考虑成员函数的调用是通过对象名还是通过基类指针或引用去访问,如果是通过基类指针或引用去访问,则应当什么为虚函数。 (4)有时在定义虚函数时,并不定义其函数体,即函数体是空的。
类A,类B,类C,其对象模型如下图3所示。 图3:类A,类B,类C的对象模型 由于这三个类都有虚函数,故编译器为每个类都创建了一个虚表,即类A的虚表(A vtbl),类B的虚表(B vtbl),类C的虚表(C vtbl)。 虽然图3看起来有点复杂,但是只要抓住“对象的虚表指针用来指向自己所属类的虚表,虚表中的指针会指向其继承的最近的一个类的虚函数”这个特点,便可以快速将这几个类的对象模型在自己的脑海中描绘出来。 虽然p是基类的指针只能指向基类的部分,但是虚表指针亦属于基类部分,所以p可以访问到对象bObject的虚表指针。bObject的虚表指针指向类B的虚表,所以p可以访问到B vtbl。如图3所示。 最后,根据虚表中找到的函数指针,调用函数。从图3可以看到,B vtbl的第一项指向B::vfunc1(),所以 p->vfunc1()实质会调用B::vfunc1()函数。
这一篇文章来讲讲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。 多态性:虚函数和纯虚函数都可以实现多态性,但纯虚函数更多地用于定义抽象接口,而虚函数则用于实现具体的多态行为。类类型:包含虚函数的类可以是普通类,也可以是抽象类;而包含纯虚函数的类一定是抽象类。
在虚函数的声明语句末尾中加个 =0 ,她就会摇身一变成为纯虚函数。 子类可以重新定义基类的虚函数,我们把这个行为称之为复写(override)。 不管是虚函数还是纯虚函数,基类都可以为提供他们的实现(implementation),如果有的话子类可以调用基类的这些实现。 子类可自主选择是否要提供一份属于自己的个性化虚函数实现。 飞行,纯虚函数 }; 这是一个普通虚函数,意味着基类希望子类提供自己的个性化实现代码,但基类同时也提供一个缺省的虚函数实现版本,在子类不复写该虚函数的情况下作为备选方案 void aircraft::refuel 虚函数和纯虚函数都能做到这一点,区别是,子类如果不提供虚函数的实现,那就会自动调用基类的缺省方案。而子类如果不提供纯虚函数的实现,则编译将会失败。 第五,虚函数和普通的函数实际上是存储在不同的区域的,虚函数所在的区域是可被覆盖(也称复写override)的,每当子类定义相同名称的虚函数时就将原来基类的版本给覆盖了,另一侧面也说明了为什么基类中声明的虚函数在后代类中不需要另加声明一律自动为虚函数