Solidity 教程系列第11篇 - Solidity 视图函数、虚函数讲解。 Solidity 系列完整的文章列表请查看分类-Solidity。 写在前面 Solidity 是以太坊智能合约编程语言,阅读本文前,你应该对以太坊、智能合约有所了解, 如果你还不了解,建议你先看以太坊是什么 视图函数(View Functions) 一个函数如果它不修改状态变量 ,应该声明为view函数,不过下面几种情况认为是修改了状态: 写状态变量 触发事件(events) 创建其他的合约 call调用附加了以太币 调用了任何没有view或pure修饰的函数 使用了低级别的调用 访问函数都被标记为view。 当前编译器并未强制要求声明为view,但建议大家对于不会修改状态的函数的标记为view。 纯函数(Pure Functions) 函数可以声明为view,表示它即不读取状态,也不修改状态,除了上一节介绍的几种修改状态的情况,以下几种情况被认为是读取了状态: 读状态变量 访问了 this.balance
浏览量 1 1.类里如果声明了虚函数,这个函数是实现的,哪怕是空实现,它的作用就是为了能让这个函数在它的子类里面可以被覆盖(override),这样的话,编译器就可以使用后期绑定来达到多态了。 纯虚函数只是一个接口,是个函数的声明而已,它要留到子类里去实现。 2.虚函数在子类里面可以不重写;但纯虚函数必须在子类实现才可以实例化子类。 3.虚函数的类用于 “实作继承”,继承接口的同时也继承了父类的实现。纯虚函数关注的是接口的统一性,实现由子类完成。 4.带纯虚函数的类叫抽象类,这种类不能直接生成对象,而只有被继承,并重写其虚函数后,才能使用。抽象类被继承后,子类可以继续是抽象类,也可以是普通类。 5.虚基类是虚继承中的基类,具体见下文虚继承。
(牛客) 思考:复制操作和虚函数有关系吗? default copy assignment operator对虚函数有什么操作? gid=28548835&egid=f9572a925d4d11ebbb08801844e2d86c ? ? ? ? 使用gdb探索 C++ 虚函数表 不同对象,创建不同的虚指针吗? 创建不同的虚函数表吗?, 一个类可以创建多个对象 在创建对象时,编译系统只为对象中的成员数据(成员变量)分配内存空间 而同类对象的成员函数的代码却是共享的。 内部的成员函数:普通函数:不占用内存。 虚函数:要占用4个字节,用来指定虚函数的虚拟函数表的入口地址。
虚函数 代码如下定义: // 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)非类的成员函数不能定义为虚函数,类的成员函数中静态成员函数和构造函数也不能定义为虚函数,但可以将析构函数定义为虚函数。 构造函数同时承担着虚函数表的建立,如果它本身都是虚函数的话,如何确保vtbl的构建成功呢? 注意:当基类的构造函数内部有虚函数时,会出现什么情况呢? 我们可以看到下面几点: 1)虚函数按照其声明顺序放于表中。 2)父类的虚函数在子类的虚函数前面。 一般继承(有虚函数覆盖) 覆盖父类的虚函数是很显然的事情,不然,虚函数就变得毫无意义。
2.1.3虚函数的重写/覆盖 虚函数的重写/覆盖:派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型、函数名字、参数类型完全相同),称派生类的虚函数重写了基类的虚函数。 debug会得不偿失,因此C++11提供了override,可以帮助用户检测是否重写。 用图来表示就如上图所示,虚函数表这里先简单提一下,下面会详细讲。 虚函数表又叫虚函数指针数组或者虚表,里面存的就是虚函数的指针。 3.派生类中重写的基类的虚函数,派生类的虚函数表中对应的虚函数就会被覆盖成派生类重写的虚函数地址。 4.派生类的虚函数表中包含,(1)基类的虚函数地址,(2)派生类重写的虚函数地址完成覆盖,派生类自己的虚函数地址三个部分。
前四个字节存储的是虚函数表的指针vfptr,后四个字节存储对象成员var的值。虚函数表的大小为4字节,就一条函数地址,即虚函数fun的地址,它在虚函数表vftable的偏移是0。 并且虚函数表内的记录多了一条——MyClassA自己定义的虚函数funA。它的对象模型如图2所示。 ? 如果子类定义了新的虚函数,虚函数表内会追加一条记录,记录该函数的地址(如MyClassA::funA)。 使用这种方式,就可以实现多态的特性。 而且每一个父类都对应一个单独的虚函数表。MyClassC的对象模型如图3所示。 ? 图3 MyClassC对象模型 多重继承下,子类不再具有自身的虚函数表,它的虚函数表与第一个父类的虚函数表合并了。 同样的,如果子类重写了任意父类的虚函数,都会覆盖对应的函数地址记录。如果MyClassC重写了fun函数(两个父类都有该函数),那么两个虚函数表的记录都需要被覆盖!
文章目录 引言: 一、虚函数的定义和使用 关于虚函数,说明以下几点: 虚函数的访问 二、纯虚函数 三、补充内容 指向类中数据成员的指针变量 例题: 引言: 若要访问派生类中相同名字的函数,必须将基类中的同名函数定义为 一、虚函数的定义和使用 可以在程序运行时通过调用相同的函数名而实现不同功能的 函数称为虚函数。 关于虚函数,说明以下几点: 1、当在基类中把成员函数定义为虚函数后,在其派生类中定义的虚函数必须与基类中的虚函数同名,参数的类型、顺序、参数的个数必须一一对应,函数的返回的类型也相同。 3、虚函数必须是类的一个成员函数,不能是友元函数,也不能是静态 的成员函数。 4、在派生类中没有重新定义虚函数时,与一般的成员函数一样,当调 用这种派生类对象的虚函数时,则调用其基类中的虚函数。 5、可把析构函数定义为虚函数,但是,不能将构造函数定义为虚函数。 6、虚函数与一般的成员函数相比较,调用时的执行速度要慢一些。
C++的虚函数是一种特殊的成员函数,用于实现多态性。虚函数允许在基类中声明一个函数,在派生类中根据需要进行重写,并通过基类指针或引用来调用派生类对象的特定实现。 ①虚函数的声明 在基类中,我们可以使用关键字virtual来声明一个虚函数。 ptr->show(); ③派生类重写虚函数 派生类可以重写基类中的虚函数,以提供自己的实现。 ." << endl; } }; ④纯虚函数 虚函数也可以被声明为纯虚函数,即没有默认实现的虚函数。纯虚函数通过在声明中使用= 0来标识。 虚函数使用动态绑定,即运行时将根据对象的实际类型选择正确的函数实现。 构造函数不能是虚函数。 静态成员函数不能是虚函数。 虚函数可以被继承,派生类可以选择是否重写虚函数。
对于经常被问到的虚函数和多态的问题,发现百度百科回答得十分详细,所以自己在百度百科上的解释进行总结 一、虚函数 (1)虚函数简介:在某基类中声明为virtual并在一个或者多个派生类中被重新定义的成员函数 <<endl; 9 } 10 }; 11 class Dog : public Animal 12 { 13 public: 14 void speak() 15 { 16 作为基类的Animal的成员函数speak( )被定义为虚函数,相应的其派生类Dog的成员函数speak( )自动变为虚函数;所以对于派生类中相应成员函数是否加上virtual关键字修饰,是可选的,但是为了可读性 (5)限制条件: 非类的成员函数不能定义为虚函数,类的成员函数中静态函数、构造函数也不能定义为虚函数,但是析构函数可以被定义为虚函数; 当基类中的某一成员函数声明为虚函数后,派生类中的同名函数(函数名相同 、参数列表完全一致、返回类型相关)自动成为虚函数; 如果声明了某个成员函数为虚函数,则在该类中不能出现和这个成员函数同名并且返回值、参数个数、类型都相同的非虚函数;在以该类为基类的派生类中,也不能出现这种同名函数
foo(); }; 有下列三种场景: SubClass::foo可能是程序员加入的一个和基类虚函数恰好同名的成员函数,却被编译器当作重载虚函数 SubClass::foo可能是程序员想重载虚函数, 但是因为形参列表不同导致编译器认为这是一个新定义的成员函数 当基类的虚函数Base::foo被删除后,SubClass::foo就不再重载该虚函数而摇身一变成为一个普通的成员函数 override 一旦类中的某个函数被声明为虚函数 ,那么在所有的派生类中它都是虚函数。 C++11新标准提供了override关键字来显式地告知虚拟器进行重载,编译器将检查基类是否存在这样的虚函数,否则将无法通过编译。 这样的好处是使得程序员的意图更加清晰(覆盖基类中的虚函数),如果我们使用override关键字标记了某个函数但是该函数没有覆盖已有的虚函数,此时编译器会报错。
(有虚函数覆盖) 4、多重继承情况(无虚函数覆盖) 5、多重继承情况(有虚函数覆盖) 四、虚函数的相关问题 1、构造函数为什么不能定义为虚函数 2、析构函数为什么要定义为虚函数? 三、虚函数的实现(内存布局) 虚函数表中只存有一个虚函数的指针地址,不存放普通函数或是构造函数的指针地址。 只要有虚函数,C++类都会存在这样的一张虚函数表,不管是普通虚函数亦或是纯虚函数,亦或是派生类中隐式声明的这些虚函数都会生成这张虚函数表。 虚函数表创建的时间:在一个类构造的时候,创建这张虚函数表,而这个虚函数表是供整个类所共有的。虚函数表存储在对象最开始的位置。虚函数表其实就是函数指针的地址。 3、单继承情况(有虚函数覆盖) 覆盖父类的虚函数是很显然的事情,不然,虚函数就变得毫无意义。下面,我们来看一下,如果子类中有虚函数重载了父类的虚函数,会是一个什么样子?
定义 纯虚函数就是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。 纯虚函数的意义在于,让所有的类对象(主要是派生类对象)都可以执行纯虚函数的动作,但类无法为纯虚函数提供一个合理的缺省实现。 纯虚函数和虚函数有什么区别 纯虚函数声明如下: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。 这里我们着重看一下这张虚函数表。在C++的标准规格说明书中说到,编译器必需要保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证正确取到虚函数的偏移量)。 那么,在派生类的实例中,其虚函数表如下所示: 对于实例:Derive d; 的虚函数表如下: 我们可以看到下面几点: 1)虚函数按照其声明顺序放于表中。 2)父类的虚函数在子类的虚函数前面。 一般继承(有虚函数覆盖) 覆盖父类的虚函数是很显然的事情,不然,虚函数就变得毫无意义。下面,我们来看一下,如果子类中有虚函数重载了父类的虚函数,会是一个什么样子? 那么,对于派生类的实例,其虚函数表会是下面的一个样子: 我们从表中可以看到下面几点, 1)覆盖的f()函数被放到了虚表中原来父类虚函数的位置。 2)没有被覆盖的函数依旧。
必须是虚函数(派生类一定要重写基类中的虚函数) ---- Q2:什么是纯虚函数,与虚函数的区别 1、定义一个函数为虚函数,不代表函数为不被实现的函数。 2、纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加"=0" 3、声明了纯虚函数的类是一个抽象类。 3、虚函数表存储虚函数的地址,即虚函数表的元素是指向类成员函数的指针,而类中虚函数的个数在编译时期可以确定,即虚函数表的大小可以确定,即大小是在编译时期确定的,不必动态分配内存空间存储虚函数表。 每一个有虚函数的类(或有虚函数的类的派生类)都有一个虚函数表,该类的任何对象中都放着该虚函数表的指针(可以认为这是由编译器自动添加到构造函数中的指令完成的)。 多态的函数调用语句被编译成根据基类指针所指向的(或基类引用所引用的)对象中存放的虚函数表的地址,在虚函数表中查找虚函数地址,并调用虚函数的一系列指令。
虚函数的使用方法(以下内容 摘自《C++面向对象程序》): (1)在基类用virtual声明成员函数为虚函数。 C++规定,当一个成员函数被声明为虚函数后,其派生类中的同名函数都自动成为虚函数。 如果在派生类中没有对基类的虚函数重新定义,派生类简单地继承其直接基类的虚函数。 (3)定义一个指向基类对象的指针变量,并使它指向同一类族中需要调用该函数的对象。 什么时候应该把一个成员函数声明为虚函数呢? (1)首先看成员函数所在的类是否会作为基类。然后看成员函数在类的继承后有无可能被更改功能,如果希望更改其功能的,一般应该讲其声明为虚函数。 (3)应该考虑成员函数的调用是通过对象名还是通过基类指针或引用去访问,如果是通过基类指针或引用去访问,则应当什么为虚函数。 (4)有时在定义虚函数时,并不定义其函数体,即函数体是空的。
关键词:虚函数,虚表,虚表指针,动态绑定,多态 一、概述 为了实现C++的多态,C++使用了一种动态绑定的技术。这个技术的核心是虚函数表(下文简称虚表)。本文介绍虚函数表是如何实现动态绑定的。 所以如果一个基类包含了虚函数,那么其继承类也可调用这些虚函数,换句话说,一个类继承了包含虚函数的基类,那么这个类也拥有自己的虚表。 我们来看以下的代码。 类A包含虚函数vfunc1,vfunc2,由于类A包含虚函数,故类A拥有一个虚表。 图1:类A的虚表示意图 虚表是一个指针数组,其元素是虚函数的指针,每个元素对应一个虚函数的函数指针。 需要指出的是,普通的函数即非虚函数,其调用并不需要经过虚表,所以虚表的元素并不包括普通函数的函数指针。
这一篇文章来讲讲C++的多态、虚函数、纯虚函数。 C++多态 多态:C++中的多态分为静态多态,动态多态。 : 您可能想要在基类中定义虚函数,以便在派生类中重新定义该函数更好地适用于对象,但是您在基类中又不能对虚函数给出有意义的实现,这个时候就会用到纯虚函数。 纯虚函数就是虚函数的函数主体=0,也就是没有主体。 { width = a; height = b; } virtual int area() = 0; } 注意: ①虚函数和纯虚函数在多态中,存在差异,虚函数在派生类中可以选择是否重写该虚函数 而纯虚函数必须在派生类中实现该纯虚函数。 ②当类中存在纯虚函数,则该类为抽象类。