C++虚函数表
- 格式:pdf
- 大小:245.73 KB
- 文档页数:2
C语言中的函数指针应用在C语言中,函数指针是一种非常强大的工具,它允许我们将函数作为参数传递给其他函数,或者将函数赋值给其他函数指针变量,从而实现更加灵活和动态的编程。
下面我们来看一些C语言中函数指针的常见应用。
1. 回调函数函数指针最常见的用途之一就是实现回调函数。
回调函数是指当某个事件发生时,通过调用事先注册好的函数来处理该事件。
例如,在GUI编程中,当用户点击按钮时,可以通过函数指针来调用相应的处理函数。
这种机制可以实现程序的灵活性和可扩展性。
2. 函数指针数组函数指针还可以用于构建函数指针数组,通过数组来存储多个函数的地址。
这样可以根据需要动态地选择并调用不同的函数。
例如,可以通过函数指针数组来实现一个简单的命令调度器,根据用户输入的命令选择执行对应的函数。
3. 函数指针作为函数返回值在C语言中,函数指针还可以作为函数的返回值。
这种情况通常发生在一些高级的应用场景中,例如函数指针用于实现函数工厂模式,根据不同的参数返回不同的函数指针,从而实现动态创建不同的函数对象。
4. 函数指针作为结构体成员函数指针也可以作为结构体的成员,用于实现结构体的多态。
这种方法类似于面向对象编程中的虚函数表,通过为结构体定义不同的函数指针来实现结构体对象的多态行为。
总的来说,函数指针是C语言中一项非常有用的特性,可以有效提高程序的灵活性和可维护性。
但是由于函数指针的复杂性和难以理解性,需要仔细考虑在何种情况下使用函数指针,以避免造成代码的混乱和难以维护。
同时,通过合理地利用函数指针,可以使程序结构更加清晰,逻辑更加严密,从而提高代码的可读性和可维护性。
C++经典⾯试题100例及答案1. ⾯向对象的程序设计思想是什么?答:把数据结构和对数据结构进⾯操作的⾯法封装形成⾯个个的对象。
2. 什么是类?答:把⾯些具有共性的对象归类后形成⾯个集合,也就是所谓的类。
3. 对象都具有的两⾯⾯特征是什么?分别是什么含义?答:对象都具有的特征是:静态特征和动态特征。
静态特征是指能描述对象的⾯些属性(成员变量),动态特征是指对象表现出来的⾯为(成员函数)4. 在头⾯件中进⾯类的声明,在对应的实现⾯件中进⾯类的定义有什么意义?答:这样可以提⾯编译效率,因为分开的话只需要编译⾯次⾯成对应的.obj⾯件后,再次应⾯该类的地⾯,这个类就不会被再次编译,从⾯⾯⾯的提⾯了编译效率。
5. 在类的内部定义成员函数的函数体,这种函数会具备那种属性?答:这种函数会⾯动为内联函数,这种函数在函数调⾯的地⾯在编译阶段都会进⾯代码替换。
6. 成员函数通过什么来区分不同对象的成员数据?为什么它能够区分?答:通过this指针指向对象的⾯地址来区分的。
7. C++编译器⾯动为类产⾯的四个缺省函数是什么?答:默认构造函数,拷贝构造函数,析构函数,赋值函数。
8. 拷贝构造函数在哪⾯种情况下会被调⾯?答:1.当类的⾯个对象去初始化该类的另⾯个对象时;2.如果函数的形参是类的对象,调⾯函数进⾯形参和实参结合时;3.如果函数的返回值是类对象,函数调⾯完成返回时。
9.构造函数与普通函数相⾯在形式上有什么不同?答:1.构造函数是类的⾯种特殊成员函数,⾯般情况下,它是专门⾯来初始化对象成员变量的。
2.构造函数的名字必须与类名相同,它不具有任何类型,不返回任何值。
10. 什么时候必须重写拷贝构造函数?答:当构造函数涉及到动态存储分配空间时,要⾯⾯写拷贝构造函数,并且要深拷贝。
11. 构造函数的调⾯顺序是什么?答:1.先调⾯基类构造函数2.按声明顺序初始化数据成员3.最后调⾯⾯⾯的构造函数。
12. 哪⾯种情况必须⾯到初始化成员列表?答:1.类的成员是常量成员初始化;2.类的成员是对象成员初始化,⾯该对象没有⾯参构造函数。
万方数据 万方数据 万方数据C++语言中的虚函数研究作者:徐启丰, 胡勇, 万玉成, XU Qifeng, HU Yong, WANG Yucheng作者单位:徐州空军学院,江苏,徐州,221000刊名:现代电子技术英文刊名:MODERN ELECTRONICS TECHNIQUE年,卷(期):2010,33(4)参考文献(14条)1.Stanley B Lippman;侯捷Inside the C++ Object Model 20012.蓝雯飞;陆际光C++面向对象程序设计中的多态性研究[期刊论文]-计算机工程与应用 2000(08)3.Bjarne Stroustrup;裘宗燕C++的设计与演化 20024.赵红超;方金云;唐志敏C++的动态多态和静态多态[期刊论文]-计算机工程 2005(20)5.蓝雯飞C++中的多态性及其应用 1998(07)6.袁亚丽;肖桂云C++中虚函数的实现技术研究[期刊论文]-河北北方学院学报(自然科学版) 2006(05)7.Scott Mayers More Effective C++ 19968.和力;吴丽贤关于C++虚函数底层实现机制的研究与分析[期刊论文]-计算机工程与设计 2008(10)9.亚鹏关于C++中虚函数的几个问题 2006(02)10.Terrence W Pratt;傅育熙程序设计语言:设计与实现 200111.张昀C++中的多态性研究 2009(02)12.Bjarne Stroustrup The C++ Programming Language 200113.夏承遗;董玉涛;赵德新C++中虚函数的实现机制[期刊论文]-天津理工学院学报 2004(03)14.蓝雯飞C++语言中的面向对象特征探讨[期刊论文]-计算机工程与应用 2000(09)本文链接:/Periodical_xddzjs201004048.aspx。
C++中虚函数工作原理和(虚)继承类的内存占用大小计算一、虚函数的工作原理虚函数的实现要求对象携带额外的信息,这些信息用于在运行时确定该对象应该调用哪一个虚函数。
典型情况下,这一信息具有一种被称为vptr(virtual table pointer,虚函数表指针)的指针的形式。
vptr 指向一个被称为vtbl(virtual table,虚函数表)的函数指针数组,每一个包含虚函数的类都关联到vtbl。
当一个对象调用了虚函数,实际的被调用函数通过下面的步骤确定:找到对象的vptr 指向的vtbl,然后在vtbl 中寻找合适的函数指针。
虚拟函数的地址翻译取决于对象的内存地址,而不取决于数据类型(编译器对函数调用的合法性检查取决于数据类型)。
如果类定义了虚函数,该类及其派生类就要生成一张虚拟函数表,即vtable。
而在类的对象地址空间中存储一个该虚表的入口,占4个字节,这个入口地址是在构造对象时由编译器写入的。
所以,由于对象的内存空间包含了虚表入口,编译器能够由这个入口找到恰当的虚函数,这个函数的地址不再由数据类型决定了。
故对于一个父类的对象指针,调用虚拟函数,如果给他赋父类对象的指针,那么他就调用父类中的函数,如果给他赋子类对象的指针,他就调用子类中的函数(取决于对象的内存地址)。
虚函数需要注意的大概就是这些个地方了,之前在More effective C++上好像也有见过,不过这次在Visual C++权威剖析这本书中有了更直白的认识,这本书名字很牛逼,看看内容也就那么回事,感觉名不副实,不过说起来也是有其独到之处的,否则也没必要出这种书了。
每当创建一个包含有虚函数的类或从包含有虚函数的类派生一个类时,编译器就会为这个类创建一个虚函数表(VTABLE)保存该类所有虚函数的地址,其实这个VTABLE的作用就是保存自己类中所有虚函数的地址,可以把VTABLE形象地看成一个函数指针数组,这个数组的每个元素存放的就是虚函数的地址。
8:下列多重继承时的二义性问题如何解决?class A{ //类A的定义public:void print () {cout<<"Hello, this is A"<<endl;}};class B{ //类B的定义public:void print () {cout<<"Hello, this is B"<<endl;}};class C : public A, public B{ //类C由类A和类B共同派生而来public:void disp () {print ();}//编译器无法决定采用A类中定义的版本还是B类中的版本};解答:若两个基类中具有同名的数据成员或成员函数,应使用成员名限定来消除二义性,如:void disp () {A: : print () ; //加成员名限定A::}print()但更好的办法是在类C中也定义一个同名print函数,根据需要调用A::还是Bprint(),从而实现对基类同名函数的隐藏::9:下列公共基类导致的二义性如何解决?class A{ //公共基类public: //public 成员列表void print(){cout << "this is x in A: " <<endl;}class B: public A{};class C: public A{};class D : public B, public C{};void main(){D d;//声明一个D类对象dA* pa=(A*) &d; //上行转换产生二义性d.print () ; //print ()具有二义性,系统不知道是调用B类的还是C类的print ()函数}注意:把子类的指针或引用转换成基类指针或引用是上行转换,把基类指针或引用转换成子类指针或引用是下行转换。
c语言实现虚函数表在C语言中实现虚函数表通常涉及结构体和函数指针的使用。
虚函数表是面向对象编程中用于实现多态的一种技术,它包含了指向各个虚函数的指针,使得在运行时能够动态地调用相应的函数。
以下是一个简单的示例来说明如何在C语言中实现虚函数表:c.#include <stdio.h>。
// 定义一个结构体来表示类。
typedef struct {。
void (func1)();void (func2)();} VirtualTable;// 定义一个类。
typedef struct {。
VirtualTable vtable;int data;} MyClass;// 定义类的成员函数。
void func1_impl() {。
printf("Function 1\n"); }。
void func2_impl() {。
printf("Function 2\n");}。
// 初始化虚函数表。
VirtualTable myClassVTable = {func1_impl, func2_impl}; // 初始化类的实例。
MyClass myObject = {&myClassVTable, 123};int main() {。
// 调用虚函数。
myObject.vtable->func1();myObject.vtable->func2();return 0;}。
在上面的示例中,我们首先定义了一个虚函数表`VirtualTable`,它包含了指向两个成员函数的指针。
然后我们定义了一个类`MyClass`,它包含了一个指向虚函数表的指针`vtable`和一些数据成员。
接着我们定义了两个成员函数`func1_impl`和`func2_impl`,并初始化了虚函数表。
最后在`main`函数中,我们通过类的实例`myObject`的虚函数表指针来调用相应的函数。
实验七虚函数及应用一、实验目的1.理解虚函数与运行时(动态)多态性之间的关系,掌握虚函数的定义及应用;2.理解纯虚函数与抽象类的概念,掌握抽象类的定义及应用;3.理解虚析构函数的概念及作用。
二、实验学时课内实验:2课时课外练习:2课时三本实验涉及的新知识㈠虚函数与动态多态性在C++中,如果将基类与派生类的同名成员函数定义为虚函数,就可以定义一个基类指针,当基类指针指向基类对象时访问基类的成员函数,当基类指针指向派生类对象时访问派生类的成员函数,实现在运行时根据基类指针所指向的对象动态调用成员函数,实现动态多态性。
换句话说,虚函数与派生类相结合,使C++能支持运行时(动态)多态性,实现在基类中定义派生类所拥有的通用“接口”,而在派生类中定义具体的实现方法,即“一个接口,多种方法”。
㈡虚函数的定义1.在基类中定义在定义函数的前面加上“virtual ”。
即:virtual 返回类型函数名(参数表){ …… }2.在派生类中定义函数的返回类型、函数名、参数的个数、参数类型及顺序必须与基类中的原型完全相同。
3.说明:⑴在派生类中定义虚函数时,可用“virtual”也可不用“virtual”(最好都使用)。
⑵虚函数在派生类中重新定义时,其原型必须与基类中相同。
⑶必须用基类指针访问虚函数才能实现运行时(动态)多态性;当用普通成员函数的调用方法(即用圆点运算符)调用虚函数时,为静态调用;⑷虚函数在自身类中必须声明为成员函数(不能为友元函数或静态成员函数),但在另一个类中可以声明为友元函数。
⑸虚函数可以公有继承多次,其虚函数的特性不变。
⑹构造函数不能定义为虚函数,但析构函数可以定义为虚函数。
⑺虚函数与重载函数的关系①普通函数重载是通过参数类型或参数的个数不同实现的;重载一个虚函数时,其函数原型(返回类型、参数个数、类型及顺序)完全相同。
②当重载的虚函数只有返回类型不同时,系统将给出错误信息;如果定义的虚函数只有函数名相同,而参数个数或类型不同时,则为普通函数重载。
虚函数和虚基类的区别 C++虚函数,纯虚函数,抽象类以及虚基类的区别Part1.C++中的虚函数什么是虚函数:直观表达就是,如果⼀个函数的声明中有 virtual 关键字,那么这个函数就是虚函数。
虚函数的作⽤:虚函数的最⼤作⽤就是实现⾯向对象程序设计的⼀⼤特点,多态性,多态性表达的是⼀种动态的概念,是在函数调⽤期间,进⾏动态绑定,以达到什么样的对象就实现什么样的功能的效果。
虚函数的⼀般声明语法:virtual 函数类型函数名 (形参表)注意:虚函数的声明只能出现在类的定义中,不能出现在成员函数实现的时候虚函数⼀般不声明为内联函数,但是声明为内联函数也不会引起错误在运⾏过程中要实现多态的三个条件:类之间满⾜赋值兼容关系(也就是类之间有继承关系)要声明为虚函数调⽤虚函数时,要由成员函数或者是指针和引⽤来访问代码举例#include <iostream>using namespace std;class Base1 {public:public:virtual void play();};void Base1::play(){cout << "Base1::play()" << endl;}class Base2:public Base1{virtual void play();};void Base2::play() {cout << "Base2::play()" << endl;}class Derived :public Base2{virtual void play();};void Derived::play() {cout << "Derived:: play()" << endl;}void fun(Base1* ba) { //声明⼀个基类的指针ba->play();}int main(){Base1 ba1;Base2 ba2;Derived de;//分别⽤不同的对象指针来调⽤ fun 函数fun(&ba1);fun(&ba2);fun(&de);return 0;}这代码含义就充分体现了虚函数作为实现多态条件的原因,由于 Base1 是 Base2 和 Derived 的⽗类,所以,Base1 是可以兼容 Base2 和Derived 的,所以在 fun 函数这⾥是⽤的 Base1 的指针来作为形参,不同的是当我传⼊参数不同时,fun 函数执⾏的是不同的结果,这就体现了多态的效果,我需要那个类型的实例,他就执⾏那个实例对应的⽅法。
虚函数表放在哪里?分类:编程语言2007-01-28 14:093224人阅读评论(5)收藏举报引言:近日CSDN的"C/C++语言"版的一个问题引起了我的注意:"请问虚函数表放在哪里?"。
我也曾经思考过这个问题,零零散散也有一定的收获,这次正好趁这个机会把我对这一部分的理解整理一下。
首先值得声明的是,本文的编译环境是VS2002+WinXP。
C++标准并没有对虚函数的实现作出任何的说明,甚至都没有提到虚函数的实现需要用虚表来实现,只不过主流的C++编译器的虚函数机制都是通过虚表来实现的,所以用虚表来实现虚函数就成了"不是标准的标准"。
但是这并不代表所有编译器在实现细节上的处理都是完全一致的,它们或多或少都存在一定的个体差异。
所以,本文的结论不一定适用于其他的编译情况。
虚函数/虚表的基础知识一个类存在虚函数,那么编译器就会为这个类生成一个虚表,在虚表里存放的是这个类所有虚函数的地址。
当生成类对象的时候,编译器会自动的将类对象的前四个字节设置为虚表的地址,而这四个字节就可以看作是一个指向虚表的指针。
虚表里依次存放的是虚函数的地址,每个虚函数的地址占4个字节。
编译模块内部虚表存放的位置如果一个模块定义了拥有虚表的类,那么这个类的虚表存放在那里呢?要回答这个问题,我们还是需要用汇编代码入手,我首先建立了一个简单的Win32 Console Application,然后定义了一个带虚函数的类,在相应的汇编代码中,我找到了重要的破解虚表存放位置的重要线索:CONST SEGMENT??_7CDerived@@6B@ ;CDerived::`vftable'DD FLAT:?foobar@CDerived@@UAEXXZDD FLAT:?callMe@CDerived@@UAEXXZ; Function compile flags: /Odt /RTCsu /ZICONST ENDS以上的汇编代码给了我们这样的信息:1> 虚表存放的位置应该实在模块的常量段中;2> 这个类有两个虚函数,它们分别是?foobar@CDerived@@UAEXXZ和?callMe@CDerived@@UAEXXZ。
1. 面向对象的程序设计思想是什么?答:把数据结构和对数据结构进行操作的方法封装形成一个个的对象。
2. 什么是类?答:把一些具有共性的对象归类后形成一个集合,也就是所谓的类。
3. 对象都具有的二方面特征是什么?分别是什么含义?答:对象都具有的特征是:静态特征和动态特征。
静态特征是指能描述对象的一些属性,动态特征是指对象表现出来的行为4. 在头文件中进行类的声明,在对应的实现文件中进行类的定义有什么意义?答:这样可以提高编译效率,因为分开的话只需要编译一次生成对应的.obj文件后,再次应用该类的地方,这个类就不会被再次编译,从而大大提高了效率。
5. 在类的内部定义成员函数的函数体,这种函数会具备那种属性?答:这种函数会自动为内联函数,这种函数在函数调用的地方在编译阶段都会进行代码替换。
6. 成员函数通过什么来区分不同对象的成员数据?为什么它能够区分?答:通过this指针来区分的,因为它指向的是对象的首地址。
7. C++编译器自动为类产生的四个缺省函数是什么?答:默认构造函数,拷贝构造函数,析构函数,赋值函数。
8. 拷贝构造函数在哪几种情况下会被调用?答:1.当类的一个对象去初始化该类的另一个对象时;2.如果函数的形参是类的对象,调用函数进行形参和实参结合时;3.如果函数的返回值是类对象,函数调用完成返回时。
9. 构造函数与普通函数相比在形式上有什么不同?(构造函数的作用,它的声明形式来分析)答:构造函数是类的一种特殊成员函数,一般情况下,它是专门用来初始化对象成员变量的。
构造函数的名字必须与类名相同,它不具有任何类型,不返回任何值。
10. 什么时候必须重写拷贝构造函数?答:当构造函数涉及到动态存储分配空间时,要自己写拷贝构造函数,并且要深拷贝。
11. 构造函数的调用顺序是什么?答:1.先调用基类构造函数2.按声明顺序初始化数据成员3.最后调用自己的构造函数。
12. 哪几种情况必须用到初始化成员列表?答:类的成员是常量成员初始化;类的成员是对象成员初始化,而该对象没有无参构造函数。
一、选择题A(1)下列的()是引用调用。
A.形参是引用,实参是变量;B.形参和实参都是变量;C.形参是指针,实参是地址值;D.形参是数组名,实参是数组名。
C(2)作用域运算符的功能是()A.给定作用域的大小;B.表示作用域的级别的;C.某个成员是属于哪个类的;D.指出作用域的范围的。
D(3)下列的各函数中,()不是类的成员函数。
A.构造函数;B.析构函数;C.拷贝初始化构造函数;D.友元函数。
D(4)下面()不是构造函数的特征。
A.构造函数可以重载;B.构造函数可以设置缺省参数;C.构造函数的函数名和类名相同;D.构造函数必须指定返回值类型。
C(5)下述静态数据成员的特性中,()是错误的。
A.静态数据成员要在类体外进行初始化;B.说明静态数据成员时前边要加修饰符static;C.静态数据成员不是所有对象所共有的;D.引用静态数据成员时,要在静态数据成员名前加<类名>和作用域运算符。
C(6)已知类A有三个公有成员:void f1(int), void f2(int) 和int a,则()是指向类A成员函数的指针。
A.A *p; B. int A::*pc=&A::a;C.void(A ::*pa)(int);D. A **p;C(7)下列关于对象数组的描述中,()是错的。
A.对象数组的数组名是一个地址常量;B.对象数组的下标是从0开始的;C.对象数组只能赋初值,不能被赋值;D.对象数组的数组元素是同一个类的对象.B(8)下列定义中,()是定义指向类A的对象数组的指针。
A.A *p[5];B.A (*p)[ 5];C.(A *) p[5]; D.A *p[ ];A(9)说明语句const char *ptr;中,ptr是()。
A.指向字符常量的指针;B.指向字符的常量指针;C.指向字符串常量的指针;D.指向字符串的常量指针。
B(10)关于new运算符的下列描述中,( )是错的.A.使用它创建对象时要调用构造函数;B.使用它创建对象数组时必须指定初始值;C.它可以用来动态创建对象和对象数组;D.使用它创建的对象或对象数组可以使用运算符delete删除。
c中virtual的作用C中virtual的作用什么是virtual?在C++中,virtual是一个关键字,用于声明类的成员函数为虚函数。
虚函数是一种特殊的成员函数,允许在继承关系中进行动态多态的调用。
virtual函数的作用1.实现多态通过将基类的成员函数声明为虚函数,可以在派生类中重写该函数,实现不同的功能。
这样,在基类指针指向派生类对象时,通过调用虚函数,可以根据实际对象的类型来调用相应的函数。
2.实现动态绑定使用虚函数可以在运行时动态绑定函数调用。
通过使用基类指针或引用指向派生类对象,可以根据实际的对象类型来调用相应的函数,而不是根据指针或引用的类型来确定函数调用。
3.实现运行时多态虚函数的另一个重要作用是实现运行时多态。
通过基类指针或引用指向不同的派生类对象,可以在运行时根据对象的具体类型来调用相应的函数,实现动态多态的效果。
使用virtual的注意事项1.virtual函数必须是成员函数虚函数必须是类的成员函数,不能是类的静态成员函数和全局函数。
2.基类中的虚函数应该有默认实现或纯虚函数基类中的虚函数可以有默认的实现,也可以声明为纯虚函数。
纯虚函数是指在基类中没有具体的实现,派生类必须实现该函数。
3.析构函数应该为虚函数如果基类中有虚函数,则析构函数应该声明为虚函数。
这是为了确保在通过基类指针删除派生类对象时,能够正确调用到派生类的析构函数。
否则,可能导致派生类的资源无法正确释放。
4.虚函数的调用开销较大虚函数的调用需要在运行时进行动态绑定,因此会有额外的开销。
对于不需要动态多态性的函数,不应该声明为虚函数,以减少运行时的开销。
总结虚函数是C++中实现多态性的重要手段之一。
通过声明虚函数,可以在派生类中重写该函数,实现动态多态的调用。
然而,虚函数的使用需要注意性能开销和函数的设计,以确保程序的正常运行和高效性能。
虚函数的实现原理虚函数的实现原理涉及到C++的对象模型和虚函数表。
在C++中,每个类对象都有一个虚函数表(vtable),虚函数表是一个指针数组,存储着该类的虚函数地址。
C与C的真正区别究竟是什么?其实作为计算机系本科⽣来说,在⼤学阶段接触最多的就是C语⾔和C++了。
C语句是⾯向结构的语⾔,C++是⾯向对象的语⾔,C++从根本上已经发⽣质飞跃,并对C进⾏丰富的扩展。
C是C++的⼦集,所以⼤部C语⾔程序都可以不加修改的拿到c++下使⽤。
那么这两者究竟有哪些区别呢,今天⼩编就跟⼤家详细探讨⼀下:C语⾔编译器内部相⽐于简单的汇编翻译器,⾃然要做更多的事情。
但是它的内核还是很机械,很⽆趣,不做哪怕⼀点点多余的事情,相⽐于汇编语⾔,不过就是可以表达的粒度⼤了⼀点点,⼀点都不智能。
不智能的意思是,它没有函数重载,没有函数重写,数据类型所蕴含的⽆穷潜⼒,在C这⾥仅仅⽤于定义内存布局,简单的类型检查。
由于缺乏⾼级的抽象机制,⽤C语⾔实在没办法搞应⽤框架这种⾼⼤上的玩意,实在是语法很不友好,抽象粒度太细,抽象⼿段太单⼀。
C语⾔这种语法简单内涵单薄的猿语,只有lognjmp和达夫设备还算有点点⼩惊喜,要精通还不是就⼿到擒来,再容易不过。
表⾯上看,C++不过是⽐C多了很多语法糖,当然,每⼀条语法糖,都代表⼀种新的抽象⼿法,表⽰写优雅的代码⼜多了⼀种选择。
⽐如说,析构函数,⽤以当对象的⽣命周期结束时将被调⽤。
析构函数的调⽤时间与函数调⽤的即时调⽤就很不⼀样,应该可以感受出来这种时间差异区别的明显。
C语⾔中没有任何办法做析构函数这样延后执⾏的⼿段,除了⼿⼯显式的在作⽤域结束之前调⽤函数,就别⽆他法了。
⽽⼤C++就⼤不⼀样,只要对象存在析构函数,只要定义对象变量,只要变量要死了,其析构函数就被调⽤。
⽽且,当有多个不同类型变量聚在⼀起,各⾃都有析构函数,编译器就会很体贴的按照栈式顺序执⾏这些对象的析构函数,这些函数调⽤的动作,从代码字⾯上看不出来,但是C++的语义就规定了这样⼀系列的动作必须发⽣。
⽽C语⾔的话,就要求程序员明确地调⽤这些析构函数,⽽且栈式顺序也续保持⼀致。
⼜⽐如说,虚函数,虚函数表,就可以将多个不同的函数打包在⼀起,这样⼦,在模板⽅法中的⼏个关键点上,同样的虚函数名称调⽤下,⼦类就各⾃做不同的动作系列。
类只有2个变量,但是大小却是12,因为此类包含了虚函数!所以,另外4个字节其实就是虚函数表指针的大小!说白了,虚函数的原理就是准备一个虚函数表的指针,这个指针指向这个类所有的虚函数,然后用这个虚函数表指针访问所有的虚函数!并且,这个指向虚函数表的指针就保存在0043DFCC 这个地址处。
可以看下面的图来得到验证!
类ClassB继承自ClassA!解释如下:指向ClassA虚函数表指针保存在009C7834这个地址,但是指向ClassB虚函数表的指针保存在009C7924这个地址处。
但是由于ClassB没有修改虚函数,那么猜测4个对象应该指向同一个虚函数其实就是ClassAVirtualFunction()这个函数,至于是不是呢?继续看图!
对了吧?4个对象共同指向了00D01091这个地址,其实说白了这个但是就是虚函数的地址。
因为子类没有改写虚函数,所以子类指向的虚函数地址和父类指向的虚函数地址是一样的,如果我们改写虚函数的话,那么子类指向的虚函数地址就应该是变化的了!
可以看到,ClassB没有改写虚函数,但是ClassC重载了父类的虚函数。
所以
ClassB和ClassA指向了相同的虚函数地址。
但是ClassC由于重写了父类的虚函数,所以指向了新的函数地址!。