-
2021-05-27 22:32:37
当基类指针指向一个子类对象,通过这个指针调用子类中和基类同名成员函数的时候,基类声明为虚函数时机会调用子类的成员函数,不声明就会调用基类。举个例子
#include <iostream> using namespace std; class A { public : virtual void foo() = 0; // { // cout << "class_A" << endl; // } }; class B: public A { public : void foo() { cout << "class_B" << endl; } }; int main() { A* a = new B(); a->foo(); //输出class_B return 0; }
有啥用呢?参考这个链接的回答知乎
定义一个叫怪物的基类,并定义一个叫攻击的虚函数,基类派生若干个子类比如金刚、浩克,重新实现’攻击‘这个函数,定义一个基类指针数组,分别指向子类,这样通过数组中的指针调用攻击函数时就会分别调用金刚和浩克的攻击函数。这里巧妙的使用了虚函数的性质,数组存储相同类型指针变量,却能调用不同子类的方法。
更多相关内容 -
Cpp-虚函数
2017-08-29 15:41:57虚函数,虚析构函数,纯虚函数,抽象类 虚函数 定义:在函数前加virtual,例子void virtual demo(); 作用:多态的重要部分 一般来说父类中的函数被子类函数重写必须采用虚函数。 class Shape { public: virtual...虚函数,虚析构函数,纯虚函数,抽象类虚函数定义:在函数前加virtual,例子void virtual demo();作用:多态的重要部分一般来说父类中的函数被子类函数重写必须采用虚函数。
虚函数在使用中的限制(重要):class Shape { public: virtual double calcArea(){...}//虚函数 .... //其他部分 private: .... }; .... class Circle:public Shape { public: Circle(double r); virtual double calcArea();//此处的virtual不是必须的,如果不加,系统会自动加 //上,如果加上则会在后续的时候看的比较明显(推荐加上) .... private: .... }; .... class Rect:public Shape { Rect(double width,double height); virtual double calcArea(); private .... }; ....
- 普通函数不能是虚函数,也就是说这个函数必须是某一个类的成员函数,不可以是一个全局函数,否则会导致编译错误。
- 静态成员函数不能是虚函数 static成员函数是和类同生共处的,他不属于任何对象,使用virtual也将导致错误。
- 内联函数不能是虚函数 如果修饰内联函数 如果内联函数被virtual修饰,计算机会忽略inline使它变成存粹的虚函数。
- 构造函数不能是虚函数,否则会出现编译错误
虚函数表指针:类中除了定义的函数成员,还有一个成员是虚函数表指针(占四个基本内存单元),这个指针指向一个虚函数表的起始位置,这个表会与类的定义同时出现,这个表存放着该类的虚函数指针,调用的时候可以找到该类的虚函数表指针,通过虚函数表指针找到虚函数表,通过虚函数表的偏移找到函数的入口地址,从而找到要使用的虚函数。
当实例化一个该类的子类对象的时候,(如果)该类的子类并没有定义虚函数,但是却从父类中继承了虚函数,所以在实例化该类子类对象的时候也会产生一个虚函数表,这个虚函数表是子类的虚函数表,但是记录的子类的虚函数地址却是与父类的是一样的。所以通过子类对象的虚函数表指针找到自己的虚函数表,在自己的虚函数表找到的要执行的函数指针也是父类的相应函数入口的地址。
如果我们在子类中定义了从父类继承来的虚函数,对于父类来说情况是不变的,对于子类来说它的虚函数表与之前的虚函数表是一样的,但是此时子类定义了自己的(从父类那继承来的)相应函数,所以它的虚函数表当中管于这个函数的指针就会覆盖掉原有的指向父类函数的指针的值,换句话说就是指向了自己定义的相应函数,这样如果用父类的指针,指向子类的对象,就会通过子类对象当中的虚函数表指针找到子类的虚函数表,从而通过子类的虚函数表找到子类的相应虚函数地址,而此时的地址已经是该函数自己定义的虚函数入口地址,而不是父类的相应虚函数入口地址,所以执行的将会是子类当中的虚函数。这就是多态的原理。
函数的覆盖和隐藏
父类和子类出现同名函数称为隐藏。
- 父类对象.函数函数名(...); //调用父类的函数
- 子类对象.函数名(...); //调用子类的函数
- 子类对象.父类名::函数名(...);//子类调用从父类继承来的函数。
父类和子类出现同名虚函数称为覆盖
- 父类指针=new 子类名(...);父类指针->函数名(...);//调用子类的虚函数。
[:->虚析构函数的特点:
- 当我们在父类中通过virtual修饰析构函数之后,通过父类指针指向子类对象,通过delete接父类指针就可以释放掉子类对象
[:->理论前提:
- 执行完子类的析构函数就会执行父类的析构函数
原理:
如果父类当中定义了虚析构函数,那么父类的虚函数表当中就会有一个父类的虚析构函数的入口指针,指向的是父类的虚析构函数,子类虚函数表当中也会产生一个子类的虚析构函数的入口指针,指向的是子类的虚析构函数,这个时候使用父类的指针指向子类的对象,delete接父类指针,就会通过指向的子类的对象找到子类的虚函数表指针,从而找到虚函数表,再虚函数表中找到子类的虚析构函数,从而使得子类的析构函数得以执行,子类的析构函数执行之后系统会自动执行父类的虚析构函数。这个是虚析构函数的实现原理。
纯虚函数
纯虚函数只提供一个接口,基类不实现纯虚函数class Shape { public: virtual double calcArea()//虚函数 {....} virtual double calcPerimeter()=0;//纯虚函数 .... };
抽象类
含有纯虚函数的类
接口类
1.没有任何数据成员
2.仅有成员函数
3.成员函数都是纯虚函数注意:若父类有虚函数,则必须实现否则无法实例化,抽象类子类只有把抽象类当中的所有的纯虚函数都做了实现才可以实例化对象。
-
cpp虚函数全解与实测
2020-09-19 18:45:06言归正传,直接给出用于测试cpp虚函数的纯虚函数,虚函数,动态多态,构造函数与析构函数的代码,各位看官应该一看便知其中的奥妙: show you my code // 虚函数动态多态 // Created by wbzhang on 2020/9/19. // #...前言…
说来许久没更新博客,但小张并没有荒废,意志也没有消退。
此刻正被老板派到河北某村儿出差,手里还没有心仪的offer,实惨。言归正传,直接给出用于测试cpp虚函数的
纯虚函数
,虚函数
,动态多态
,构造函数
与析构函数
的代码,各位看官应该一看便知其中的奥妙:show you my code
// 虚函数动态多态 // Created by wbzhang on 2020/9/19. // #include <iostream> #include <string> using namespace std; struct Point2i { int x,y; }; // 基类 Shape class Shape { protected: int width; int height; public: static string ShapeName; // 静态成员 static void shapePrint() { std::printf("ShapeName is %s in stacic.","shape"); } void setWidth(int w) { width = w; } void setHeight(int h) { height = h; } virtual void print() { cout<<"This is shape"<<endl; } // 构造函数 Shape():width(0),height(0) { cout<<"This Shape is constructed."<<endl; } // 父类析构函数申明为虚函数 virtual ~Shape() { cout<<"This Shape is destroyed."<<endl; } }; // 接口类,仅包含纯虚函数,并且不包含其他任何成员与方法 // 无法进行实例化 class PaintMethod { public: virtual void getCube()=0; virtual void getPencil()=0; virtual void getBlank()=0; }; // 基类 PaintCost,抽象类;无法进行实例化 class PaintCost { public: int getCost(int area) { return area * 70; } // 声明一个纯虚函数 virtual void getCube() =0; }; // 还有一种所有方法均public给外界调用的叫啥...给忘了,集成类?? // 派生类 class Rectangle: public Shape, public PaintCost { private: Point2i startPt; Point2i endPt; public: int getArea() { return (width * height); } void print() { cout<<"This is Rectangle"<<endl; } // 继承了抽象类的纯虚函数,则必须将所有纯虚函数均实现才可以实例化 void getCube() { cout<<"Just test for pure_virtual_class."<<endl; } // 子类构造函数 Rectangle() { cout<<"This Rectangle is constructed."<<endl; } // 子类析构函数 ~Rectangle() { cout<<"This Rectangle is destroyed."<<endl; } }; int main(void) { // 1.构造函数 cout<<"------ Shape constructor func ------"<<endl; Shape *shape = new Shape(); cout<<"------ Rectangle constructor func ------"<<endl; Rectangle *rectangle = new Rectangle(); // 1.1 调用自身打印 cout<<"------ Self print ------"<<endl; cout<<"Shape print: "; shape->print(); cout<<"Rectangle print: "; rectangle->print(); // 1.2 类的静态成员与静态方法,通过::获取 // Shape::ShapeName = "I'm BJTUer."; // cout<<Shape::ShapeName<<endl; // Shape::shapePrint(); // 2 动态多态打印 cout<<"------ Dynamic constructor func ------"<<endl; Shape *shape1 = new Rectangle(); cout<<"------ Dynamic bind print ------"<<endl; shape1->print(); // 3 调用析构函数 cout<<"------ shape destructor func------"<<endl; delete shape; cout<<"------ rectangle destructor func------"<<endl; delete rectangle; cout<<"------ shape1 destructor func------"<<endl; delete shape1; return 0; } /* 总结: * 1) 虚函数原理:虚表指针与虚函数表 * 2) 构造函数:先基类,再派生类的成员,再调用派生类构造函数 * 3) 析构函数:先调用子类析构函数,再调用父类析构函数 * 4) 纯虚函数-->接口类,抽象类 * */
测试结果如下:
总结
-
虚函数原理:虚表指针与虚函数表
-
构造函数:先基类,再派生类的成员,再调用派生类构造函数
-
析构函数:先调用子类析构函数,再调用父类析构函数
-
纯虚函数–>接口类,抽象类
-
-
cpp代码-基类调用虚函数的动态绑定
2021-07-14 22:44:08cpp代码-基类调用虚函数的动态绑定 -
C/Cpp / C++ 构造函数和析构函数可以是虚函数吗
2021-04-06 10:40:16虚函数表是由编译器自动生成与维护的,virtual成员函数会被编译器放入虚函数表中,当存在虚函数时,每个对象都有一个指向虚函数的指针(vptr指针)。在实现多态的过程中,父类和派生类都有vptr指针。 vptr的初始化...答案
构造函数不可以是虚函数,而析构函数可以且常常是虚函数。
原因
1、构造函数不可以是虚函数
当类中声明虚函数时,编译器会在类中生成一个虚函数表,虚函数表是一个存储成员函数指针的数据结构。
虚函数表是由编译器自动生成与维护的,virtual成员函数会被编译器放入虚函数表中,当存在虚函数时,每个对象都有一个指向虚函数的指针(vptr指针)。在实现多态的过程中,父类和派生类都有vptr指针。
vptr的初始化:当对象在创建时,由编译器对 vptr 指针进行初始化。在定义子类对象时,vptr 先指向父类的虚函数表,在父类构造完成之后,子类的 vptr 才指向自己的虚函数表。
如果构造函数时虚函数,那么调用构造函数就需要去找vptr,而此时 vptr 还没有初始化。
因此,构造函数不可以是虚函数。
2、析构函数常常是虚函数
与构造函数不同,vptr 已经完成初始化,析构函数可以声明为虚函数,且类有继承时,析构函数常常必须为虚函数。
比如下面的例子:
#include <iostream> using namespace std; class base { public: base() { cout << "base constructor" << endl; } virtual ~base() { cout << "base destructor" << endl; } }; class derived : public base { public: derived() { cout << "derived constructor" << endl; } virtual ~derived() { cout << "derived destructor" << endl; } }; int main() { base *pBase = new derived; cout << "---" << endl; delete pBase; return 0; }
运行结果为:
若析构函数不是虚函数,则运行结果为:
可以看出:
- 若析构函数是虚函数,delete 时,基类和子类都会被释放;
- 若析构函数不是虚函数,delete 时,只有基类会被释放,而子类没有释放,存在内存泄漏的隐患。
详情请看这里。
(SAW:Game Over!)
-
C/Cpp / 模板类中可以使用虚函数吗?模板成员函数可以是虚函数吗?
2021-04-06 10:16:41二、原因 编译器都期望在处理类的定义的时候就能确定这个类的虚函数表的大小,如果允许有类的虚成员模板函数,那么就必须要求编译器提前知道程序中所有对该类的该虚成员模板函数的调用,而这是不可行的。... -
虚函数,纯虚函数,抽象类,点H与点CPP文件的结合
2016-08-23 08:07:08虚函数,纯虚函数,抽象类,经典例题,点H与点CPP文件的结合,声明与定义 -
解析C++编程中virtual声明的虚函数以及单个继承
2021-01-20 06:54:52虚函数 虚函数是应在派生类中重新定义的成员函数。 当使用指针或对基类的引用来引用派生的类对象时,可以为该对象调用虚函数并执行该函数的派生类版本。 虚函数确保为该对象调用正确的函数,这与用于进行函数调用的... -
C++类的虚函数虚继承所占的空间
2020-12-22 22:57:09GCC中, 无论是虚函数还是虚继承, 都需要将指针存储在虚函数表(virtual function table), 占用4个字节. 继承会继承基类的数据, 和虚函数表, 即继承基类的空间. 代码: /* * test.cpp * * Created ... -
博客虚函数表的作用.rar
2019-11-05 12:59:45博客《Cpp 对象模型探索 —— 虚函数表作用》中图片的原版文档,网址:https://blog.csdn.net/itworld123/article/details/102894752 。 -
深入探讨C++父类子类中虚函数的应用
2021-01-20 06:31:16构造函数不能是虚函数,因为在调用构造函数创建对象时,构造函数必须是确定的,所以构造函数不能是虚函数。析构函数可以是虚函数。1.父类Father.h: 代码如下:#pragma onceclass Father{public: Father(void); ... -
虚函数指针 虚函数表
2020-02-04 14:02:42虚函数指针和虚函数表 1.虚函数的含义 只有用virtual声明类的成员函数,称之为虚函数。 2.虚函数的作用 就是一句话:实现多态的基石 实现多态的三大步: 1.存在继承关系 子类继承父类 2.子类重写父类的virtual ... -
Cpp--虚函数的存在
2015-11-09 14:01:421.虚函数基础: 定义:在某基类中声明为 virtual 并在一个或多个派生类中被重新定 义的成员函数。 用法格式:virtual 函数返回类型 函数名(参数表) {函数体} 作用:它是实现多态性,通过指向派生类的基类指针或... -
class.cpp多态虚函数的演示课设
2020-02-07 15:24:41多态虚函数的运行演示 -
Cpp_ShapesOOP:使用形状探索OOP原理,多线程和虚函数
2021-04-06 06:40:20Cpp_ShapesOOP 使用形状探索OOP原理,多线程和虚函数 像在线使用ppm查看器来查看输出文件。 -
【cpp中的虚函数与继承个人笔记总结】
2018-10-12 00:02:48继承: 继承主要实现重用代码,节省开发时间。 1、C#中的继承符合下列规则: ...如果C从B中派生,B又从A中派生,那么C不仅继承了B中声明的成员,同样也继承了A中的成员。...派生类可以添加新的成员,但不能除... -
C++ 虚函数和抽象类的简单应用(cpp&h文件)
2015-07-08 20:58:27C++ ,VS2010平台编辑,下载后可以直接使用,包含头文件和源文件 -
cpp-多态和虚函数
2020-06-20 18:02:26不使用虚函数可以实现多态吗? #include <iostream> #include <stdlib.h> using namespace std; class father{ public: void print(){ cout << "father" << endl; } }; class son:... -
C++多态虚函数实现原理,对象和虚函数表的内存布局
2022-05-17 21:25:43我们知道C++动态多态是用虚函数实现的,而虚函数的实现方式虽说C++标准没有要求,但是基本都是用虚函数表实现的(编译器决定)。所以我们有必要了解一下虚函数表的实现原理。 用virtual关键字声明的成员函数是虚... -
什么是C++虚函数、虚函数的作用和使用方法
2019-03-13 10:45:33转自:http://c.biancheng.net/cpp/biancheng/view/244.html 我们知道,在同一类中是不能定义两个名字相同、参数个数和类型都相同的函数的,否则就是“重复定义”。但是在类的继承层次结构中,在不同的层次中可以... -
C++中的Virtual Function (虚函数)
2020-12-22 17:50:38还没有涉及到构造函数。那么直接上代码了: // VitualFunction.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include #include using namespace std; //base ... -
Cpp 对象模型探索 / 虚函数表和虚函数表指针的创建时机
2019-10-24 06:10:05一、虚函数表 在编译期间创建。编译器会为每个类确定好虚函数表(vtbl)的内容。 二、虚函数表指针 虚函数表指针跟随着对象,在运行期间创建。由于在编译期间编译器为每个类创建好了 vtbl,并且编译器会在类的... -
对于新增Cpp虚函数的引起的偏移问题
2020-07-18 12:31:17C++类已定义的虚函数位置不能随意变动,一般建议在所有的虚函数末尾添加 并且private属性也没有作用 场景: 如发布头文件与so给应用; 之后在新发布的so中在原有头文件的虚函数A前面增加了一个虚函数B定义; 应用... -
C++面向对象程序设计CPP程序设计9:多态性和虚函数.pdf
2020-05-05 12:46:34The C++ Programming Language 面向对象的程序设计 Object-oriented Programming C++ 程序设计 第9讲 多态性和虚函数 Chapter 9 Polymorphism and virtual function 第9章 多态性和虚函数 polymorphism and virtual ... -
虚函数和普通函数的区别(c++)
2022-04-19 17:19:49} 多态,基类指针的眼睛就只能看那么大的地方 三种:基类指针,虚函数,子类重写父类的虚函数 注意:(1)如果没有指针,父类对象调用就是父类中的成员函数,子类对象调用的就是子类中的成员函数,这个不能多态 (2... -
C++ | 虚函数表内存布局
2020-02-19 23:41:32存在虚函数的类会在类的数据成员中生成一个虚函数指针 vfptr,而vfptr 指向了一张表(简称,虚表)。正是由于虚函数的这个特性,C++的多态才有了发生的可能。 其中虚函数表由三部分组成,分别是 RTTI(运行时类型... -
C++、类继承与虚函数
2022-04-11 10:07:50类继承与虚函数的一些基本概念。 -
c++虚函数详解及其实现
2019-03-15 15:47:14虚函数: 虚函数是一种在基类定义为virtual的函数,并在一个或多个派生类中再定义的函数。虚函数的特点是,只要定义一个基类的指针,就可以指向派生类的对象。 注:无虚函数时,遵循以下规则:C++规定,定义为基类的...