虚函数

C++虚函数表和对象存储

天大地大妈咪最大 提交于 2019-11-30 13:33:30
C++虚函数表和对象存储 C++中的虚函数实现了多态的机制,也就是 用父类型指针指向其子类的实例 ,然后通过父类的指针调用实际子类的成员函数,这种技术可以让父类的指针有“多种形态”,这也是一种泛型技术,也就是 使用不变的代码来实现可变的算法 本文不再阐述虚函数的使用方法,而是从虚函数的实现机制上做一个清晰的剖析 参考博文: https://blog.csdn.net/u012879957/article/details/81027287 想了解实现机制,就必须先了解对象的存储方式 对象数据和函数的存储方式 我们知道,用类去定义对象,系统会为每一个对象分配存储空间 在你的印象中类的存储可能是下图这样的: 上图表示要分别为对象的数据和函数的代码分配存储空间,这样肯定是不行的,内存利用率太低了,所以C++编译系统采用的是以下的方法: 每个对象占用存储空间的只是该对象的数据部分(虚函数指针和虚基类指针也属于数据部分),函数代码属于公用部分 我们常说的“A对象的成员函数”,是从逻辑的角度而言的,而成员函数的物理存储方式其实不是如此 C++内存分区 C++的内存分区大概分成五个部分: 栈(stack):是由编译器在需要时自动分配,不需要时自动清除的变量存储区,通常存放局部变量、函数参数等。 堆(heap):是由 new 分配的内存块,由程序员释放(编译器不管),一般一个 new 与一个

嵌入式C++测试题

早过忘川 提交于 2019-11-30 10:30:18
仅供学习,为嵌入式帝国做亿分之一的贡献吧,临近国庆,祝大家国庆节快乐哦 答案仅供参考吧 一、 选择题(共 80 分, 每题 2 分 ) ( 1 ) A ( 2 ) C ( 3 ) C ( 4 ) B ( 5 ) D ( 6 ) C ( 7 ) C ( 8 ) D ( 9 ) A ( 10 ) C ( 11 ) A (12) B (13) D (14) B (15) A ( 16 ) A ( 17 ) C ( 18 ) D ( 19 ) D ( 20 ) C ( 21 ) C ( 22 ) D ( 23 ) D ( 24 ) D ( 25 ) A ( 26 ) C (27) C (28) C (29) B (30) B ( 31 ) A ( 32 ) D ( 33 ) D ( 34 ) B ( 35 ) A ( 36 ) A ( 37 ) B ( 38 ) B ( 39 ) C ( 40 ) C 二、 填空题(每空 1 分, 共 20 分) 1. 构造函数 2. friend 保护和私有成员 3. 先基类在派生类 4. 单一继承 5. virtual 6. 静态多态性 动态多态性 7. 抽象类 8. 测试是否文件尾 9. 成员函数 10. try throw catch 11. 代码复用和泛型程序设计 12. 类的友元函数 13. 抛出异常 捕捉异常 14. template 15.

C#中的虚函数virtual

≯℡__Kan透↙ 提交于 2019-11-30 07:55:02
简单介绍虚函数virtual 在某基类中声明 virtual 并在一个或多个派生类中被重新定义的成员函数称为虚函数。 虚函数的作用就是实现多态性(Polymorphism),多态性是将接口与实现进行分离。 C#作为完全面向对象语言,所有函数并不默认为virtual,但可以在基类中声明关键字virtual,就可以在其派生类中通过关键字override重写该函数。重写后的virtual函数依旧是virtual函数。由于virtual只对类中的实例函数成员有意义,所以成员字段和静态函数都不能声明为virtual,也不能与override和abstract一起用。 虚函数与一般函数的区别 一般函数在编译时就静态地编译到了执行文件中,其相对地址在程序运行期间是不发生变化的,也就是写死的!而虚函数在编译期间是不被静态编译的,它的相对地址是不确定的,它会根据运行时期对象实例来动态判断要调用的函数,其中声明时定义的类叫声明类,执行时实例化的类叫实例类。 使用虚函数virtual 1、当调用一个对象的函数时,系统会直接去检查这个对象声明定义的类,即声明类,看所调用的函数是否为虚函数。 2、如果不是虚函数,那么它就直接执行该函数。而如果有virtual关键字,也就是一个虚函数,那么这个时候它就不会立刻执行该函数了,而是转去检查对象的实例类。 3、在这个实例类里

C++对象模型:单继承,多继承,虚继承,菱形虚继承,及其内存布局图

放肆的年华 提交于 2019-11-29 22:02:48
C++目前使用的对象模型: 此模型下, nonstatic数据成员被置于每一个类的对象中 ,而 static数据成员则被置于类对象之外 , static和nonstatic函数也都放在类对象之外(通过函数指针指向) ,而 对于virtual函数,则通过虚函数表+虚函数指针来支持 : 1)每个类生成一个表格,称为 虚表 (virtual table,简称vtbl),虚函数表中存在一堆指针,这些指针指向该类的每一个虚函数,虚表中的函数地址按照声明时的顺序排列 2)每个类对象都有一个 虚表指针 (简称vptr),由编译器为其生成,虚表指针的设定和重置皆由类的相关函数控制(构造函数,析构函数,赋值操作符),虚表指针vptr的位置由编译器决定,一般编译器把vptr放在一个类对象的最前端(也就是说对象的地址就是vptr的地址) 3)虚函数表的前面设置了一个指向type-info的指针,用以支持RTTI(Run Time Type Identification,运行时类型识别),RTTI是为支持多态生成的信息,包括对象的继承关系,对象本身的描述等,只有具有虚函数的对象才会生成 样例: class Base { public: Base(int i) :baseI(i){}; int getI(){ return baseI; } static void countI(){}; virtual

c/c++常见面试题(一)

佐手、 提交于 2019-11-29 21:39:57
0.static、const、volatile的作用和区别 static: https://www.cnblogs.com/Manual-Linux/p/8870038.html 第一 、在修饰变量的时候,static修饰的静态局部变量只执行一次,之后再初始化无效。而且延长了局部变量的生命周期,直到程序运行结束以后才释放。 第二 、static修饰全局变量的时候,这个全局变量只能在本文件中访问,不能在其它文件中访问,即便是extern外部声明也不可以。 第三 、static修饰一个函数,则这个函数的只能在本文件中调用,不能被其他文件调用。Static修饰的局部变量存放在全局数据区的静态变量区。初始化的时候自动初始化为0 const: 1.修饰的变量只读,不可改变 2.防止意外修改,减少bug 3.可以节省空间,避免不必要的 内存分配 volatile: 告诉编译器这个变量随时可变,不需要优化 1.请问全局变量和局部变量能否重名 能,局部会屏蔽全局。要用全局变量,需要使用"::" 2. 用三目运算符(X)>(Y)?(X):(Y)宏定义实现比较两个数的大小 #define MAX(X, Y) ((X)>(Y)?(X):(Y)) 3.malloc的了解 malloc是动态内存分配,是用户动态申请系统分配指定字节的内存块的函数。返回类型是 void* 类型 对应free,

c++多态实现与虚函数

血红的双手。 提交于 2019-11-29 19:32:59
#include <stdlib.h> #include <stdio.h> #include <iostream> #include <bitset> using namespace std; //如果delete后边跟父类的指针则只会执行父类的析构函数,如果delete后面跟的是子类的指针,那么它即会执行子类的析构函数,也会执行父类的析构函数 //virtual在函数中的使用限制 // 普通函数不能是虚函数,也就是说这个函数必须是某一个类的成员函数,不可以是一个全局函数,否则会导致编译错误。 // 静态成员函数不能是虚函数 static成员函数是和类同生共处的,他不属于任何对象,使用virtual也将导致错误。 // 内联函数不能是虚函数 如果修饰内联函数 如果内联函数被virtual修饰,计算机会忽略inline使它变成存粹的虚函数。 // 构造函数不能是虚函数,否则会出现编译错误。 //将父类的的析构函数设置成虚函数,这样父类指针指向的是哪个对象,哪个对象的构造函数就会先执行,然后执行父类的构造函数。销毁的时候子类的析构函数也会执行。 class A { private: int m; public: void first() { printf("A1\n"); } virtual void second() { printf("A2\n"); } A(const A&

C++ 类的大小

随声附和 提交于 2019-11-29 18:21:53
1.类的大小与什么有关系? 与类大小有关的因素:普通成员变量,虚函数,继承(单一继承,多重继承,重复继承,虚拟继承) 与类大小无关的因素:静态成员变量,静态成员函数及普通成员函数 2.空类 空类即什么都没有的类,按上面的说法,照理说大小应该是0,但是,空类的大小为1,因为空类可以实例化,实例化必然在内存中占有一个位置,因此,编译器为其优化为一个字节大小。 某类继承自空类: class base { }; class derived:public base { private: int a; } 此时,derived类的大小为4,derived类的大小是自身int成员变量的大小,至于为什么没有加上父类base的大小1是因为空白基优化的问题,在空基类被继承后,子类会优化掉基类的1字节的大小,节省了空间大小,提高了运行效率。 3.一般类的大小(注意内存对齐) 首先上两个类的示例: class base1 { private: char a; int b; double c; }; class base2 { private: char a; double b; int c; }; 虽然上述两个类成员变量都是一个char,一个int,一个double,但是不同的声明顺序,会导致不同的内存构造模型,对于base1,base2,其成员排列是酱紫的: base1: base2: base

C++的构造函数为何不能为虚函数

☆樱花仙子☆ 提交于 2019-11-29 17:40:44
1. 存储空间角度:虚函数对应一个vtable,vtable存储于对象的内存空间 若构造函数是虚的,则需要通过 vtable来调用,若对象还未实例化,即内存空间还没有,无法找到vtable 2. 使用角度:虚函数主要用于在信息不全的情况下,能使重载的函数得到对应的调用。 构造函数本身就是要初始化实例,那使用虚函数就没有实际意义 3. 从实际含义上看,在调用构造函数时还不能确定对象的真实类型(因为子类会调父类的构造函数);而且构造函数的作用是提供初始化,在对象生命期只执行一次,不是对象的动态行为,也没有太大的必要成为虚函数 来源: https://www.cnblogs.com/sylar5/p/11525113.html

C++构造函数和析构函数调用虚函数时都不会使用动态联编

六眼飞鱼酱① 提交于 2019-11-29 17:11:05
可以,虚函数底层实现原理(但是最好不要在构造和析构函数中调用) 可以,但是没有动态绑定的效果,父类构造函数中调用的仍然是父类版本的函数,子类中调用的仍然是子类版本的函数。 effictive c++第九条,绝不在构造和析构过程中调用virtual,因为构造函数中的base的虚函数不会下降到derived上。而是直接调用base类的虚函数。绝不在构造和析构函数中调用virtual函数: a) 如果有继承,构造函数会先调用父类构造函数,而如果构造函数中有虚函数,此时子类还没有构造,所以此时的对象还是父类的,不会触发多态。更容易记的是基类构造期间,virtual函数不是virtual函数。 b) 析构函数也是一样,子类先进行析构,这时,如果有virtual函数的话,子类的内容已经被析构了,C++会视其父类,执行父类的virtual函数。 c) 总之,在构造和析构函数中,不要用虚函数。如果必须用,那么分离出一个Init函数和一个close函数,实现相关功能即可。 原文链接:https://blog.csdn.net/chen134225/article/details/81564972 第一个理由是概念上的。 在概念上,构造函数的工作是生成一个对象。在任何构造函数中,可能只是部分形成对象——我们只能知道基类已被初始化,但并不能知道哪个类是从这个基类继承来的。然而,虚函数在继承层次上是“向前