虚函数总结
来源:互联网 发布:电脑开热点软件 编辑:程序博客网 时间:2024/05/21 05:55
多态(Polymorphism),是面向对象程序设计(OOP,Object-Oriented Programming)的重要特征。
多态的定义:
多态的实现方式有:
(1)函数重载
(2)运算符重载
(3)模版
(4)虚函数
前三种我们称为静态绑定,第四种称为动态绑定。
静态绑定:绑定过程出现在编译阶段,在编译期就已确定要调用的函数;
动态绑定:绑定过程工作在运行阶段时执行,在程序运行时才确定将要调用的函数。
(只有通过基类指针或引用调用虚函数才能引发动态绑定)
虚函数的概念:在基类中冠以关键字“virtual”的成员函数。
虚函数的定义:
(1)virtual 函数返回值类型函数名称 (参数列表)
(2)如果一个函数在基类中被声明为虚函数,则他在所有派生类中都是虚函数。
值得注意的是:
虚函数不能声明为静态函数,也不能是友元函数。
基类指针指向派生类对象:
//// VF2.cpp// C++//// Created by Cheng Sun on 2017/6/21.// Copyright © 2017年 Cheng Sun. All rights reserved.//#include <stdio.h>#include <iostream>using namespace std;class Base{public: virtual void Func1() { cout << "Base::Func1()..." << endl; } virtual void Func2() { cout << "Base::Func2()..." << endl; } void Func3() { cout << "Base::Func3()..." << endl; }};class Derived : public Base{public: /*virtual*/ void Func1() { cout << "Derived::Func1()..." << endl; } /*virtual*/ void Func2() { cout << "Derived::Func2()..." << endl; } void Func3() { cout << "Derived::Func3()..." << endl; }};int main(){ Base* p; Derived d; p = &d; // 基类指针指向派生类对象 p->Func1(); //Func1是虚函数,基类指针指向派生类对象,调用的是派生类对象的虚函数。 p->Func2(); p->Func3(); //Func3非虚函数,根据p指针实际类型来调用相应类的成员函数。 return 0;}程序运行结果:
Derived::Func1()...Derived::Func2()...Base::Func3()...Program ended with exit code: 0
虚析构函数
同样的道理,也反映在析构函数的应用上。当基类指针指向派生类对象,我们delete基类指针时,若基类的析构函数的不是虚函数,则只会释放基类对象的内存空间。想要同时释放派生类的内存空间,则需要将析构函数设为虚函数。(同样的,只需要将基类的析构函数设为虚函数,则派生类的析构函数也会自动变成虚函数。)
//// VF2.cpp// C++//// Created by Cheng Sun on 2017/6/21.// Copyright © 2017年 Cheng Sun. All rights reserved.//#include <stdio.h>#include <iostream>using namespace std;class Base{public: virtual void Func1() { cout << "Base::Func1()..." << endl; } virtual void Func2() { cout << "Base::Func2()..." << endl; } void Func3() { cout << "Base::Func3()..." << endl; } Base() { cout << "Base()..." << endl; } virtual ~Base() { cout << "~Base()..." << endl; }};class Derived : public Base{public: /*virtual*/ void Func1() { cout << "Derived::Func1()..." << endl; } /*virtual*/ void Func2() { cout << "Derived::Func2()..." << endl; } void Func3() { cout << "Derived::Func3()..." << endl; } Derived() { cout << "Derived()..." << endl; } ~Derived() { cout << "~Derived()..." << endl; }};int main(){ Base* p; p = new Derived; // 创建派生类对象的时候,会首先创建它的基类对象 p->Func1(); delete p; return 0;}程序运行结果:
Base()...Derived()...Derived::Func1()...~Derived()...~Base()...Program ended with exit code: 0
如果上面代码中的基类没有将析构函数设为虚函数,那么delete p只会执行~Base(),而不会执行~Derived()。
总的来说,什么时候需要用到虚析构函数呢?如果一个类要作为多态基类,那么就要将析构函数定义为虚函数。
反之,如果一个类不会被任何派生类继承,则没有必要将它的析构函数定义为虚函数。
虚表指针和虚函数表
当一个类包含一个或一个以上的虚函数时,编译器就会为这个类产生一个4个字节(编译器工作在32位的框架下)的虚表指针。
虚函数是通过虚表来实现的。讨论没有覆盖的虚函数的情况没有太多意义,我们讨论一下有覆盖的虚函数的工作机制。当一个派生类继承基类时,若是派生类中出现了基类中同名的虚函数,即发生了所谓的覆盖,那么在派生类的虚函数表中,覆盖的虚函数被放到了原本父类虚函数的位置,没有被覆盖的函数,位置依旧。
虚表指针存放在类对象内部的头四个字节,三大区域都有可能。
虚函数表存放在全局数据区(静态区),因为对每个类,所有该类的对象共用虚表。
为方便下面部分代码的理解,这里稍稍介绍一下定义一个函数指针的方法:
返回类型 (*指针名) (函数参数列表)
例如: void (*Fun)(void) 意思是定一个指向返回值为void,函数参数也为void的函数的指针。
探讨包含虚函数的基类和派生类的内存模型:
//// VF3.cpp// C++//// Created by Cheng Sun on 2017/6/21.// Copyright © 2017年 Cheng Sun. All rights reserved.//#include <stdio.h>#include <iostream>using namespace std;class Base // 4 Bytes (pointer to virtual table) + 4 Bytes (int) = 8 Bytes{public: virtual void Func1() { cout << "Base::Func1()..." << endl; } virtual void Func2() { cout << "Base::Func2()..." << endl; } virtual void Func3() { cout << "Base::Func3()..." << endl; } int data1_;};class Derived : public Base // 4 Bytes (pointer to virtual table) + 4 Bytes (int, data1_) + 4 Bytes (int, data2_) = 12 Bytes{public: void Func2() { cout << "Derived::Func2()..." << endl; } void Func3() { cout << "Derived::Func3()..." << endl; } int data2_;};typedef void (*FUNC) (void); //定义一个函数指针,指向一个函数,这函数的类型是 void Fun (void)int main(){ cout << "size of Base = " << sizeof(Base) << endl; cout << "size of Derived = " << sizeof(Derived) << endl; Base b; Derived d; long** p = (long**)&b; //定义一个p, 指向虚表指针的指针 FUNC fun; cout << "基类的虚函数表如下: " << endl; fun = (FUNC)p[0][0]; fun(); //函数指针 == 函数地址 == 函数名 ? fun = (FUNC)p[0][1]; fun(); fun = (FUNC)p[0][2]; fun(); cout << "派生类的虚函数表如下: " << endl; p = (long**)&d; fun = (FUNC)p[0][0]; fun(); fun = (FUNC)p[0][1]; fun(); fun = (FUNC)p[0][2]; fun(); cout << "----------------" << endl; Base* pp = &d; pp->Func2(); // 基类指针指向派生对象,通过基类指针(或引用)调用派生类虚函数,触发动态绑定 d.Func2(); // 直接调用,不会触发动态绑定,属于静态绑定 return 0;}程序运行结果:
size of Base = 8size of Derived = 12基类的虚函数表如下: Base::Func1()...Base::Func2()...Base::Func3()...派生类的虚函数表如下: Base::Func1()...Derived::Func2()...Derived::Func3()...----------------Derived::Func2()...Derived::Func2()...Program ended with exit code: 0
多重继承中,包含虚函数的基类和派生类的内存模型:
因为是多重继承,不同于单一继承,所以派生类的虚表指针也不止一个,所以代码中可以访问p[1][n],p[2][n],而如果是单一继承,则只有p[0][n]。
验证代码如下:
//// VF3.cpp// C++//// Created by Cheng Sun on 2017/6/21.// Copyright © 2017年 Cheng Sun. All rights reserved.//#include <stdio.h>#include <iostream>using namespace std;class Base1 // 4 Bytes (pointer to virtual table) + 4 Bytes (int) = 8 Bytes{public: virtual void Func1() { cout << "Base1::Func1()..." << endl; } virtual void Func2() { cout << "Base1::Func2()..." << endl; } virtual void Func3() { cout << "Base1::Func3()..." << endl; } int data1_ = 1;};class Base2 // 4 Bytes (pointer to virtual table) + 4 Bytes (int) = 8 Bytes{public: virtual void Func1() { cout << "Base2::Func1()..." << endl; } virtual void Func2() { cout << "Base2::Func2()..." << endl; } virtual void Func3() { cout << "Base2::Func3()..." << endl; } int data2_ = 2;};class Base3 // 4 Bytes (pointer to virtual table) + 4 Bytes (int) = 8 Bytes{public: virtual void Func1() { cout << "Base3::Func1()..." << endl; } virtual void Func2() { cout << "Base3::Func2()..." << endl; } virtual void Func3() { cout << "Base3::Func3()..." << endl; } int data3_ = 3;};class Derived : public Base1, public Base2, public Base3// 4 Bytes (pointer to virtual table) * 3 + 4 Bytes (int, data1_) * 3 + 4 Bytes (int, data4_) = 28 Bytes{public: // four virtual funcitons in derived class void Func2() { cout << "Derived::Func2()..." << endl; } void Func3() { cout << "Derived::Func3()..." << endl; } virtual void Func4() { cout << "Derived::Func4()... 此函数是派生类特有的哦,只存在派生类覆盖第一个基类的虚表里" << endl; } int data4_ = 4;};typedef void (*FUNC) (void); //定义一个函数指针,指向一个函数,这函数的类型是 void Fun (void)int main(){ cout << "size of Base = " << sizeof(Base1) << endl; cout << "size of Derived = " << sizeof(Derived) << endl; long** p; FUNC fun; int* da; //既然当一个类包含虚函数时,系统就会为它自动生成一个虚函数表 //那么一个类就一个表?如果对于继承了好几个基类的派生类呢? //如果不是,那么继承了多个基类的派生类有多少个虚表指针? cout << "-----------------" << endl; cout << "第一个基类的虚函数表如下: " << endl; Base1 b1; p = (long**)&b1; fun = (FUNC)p[0][0]; fun(); fun = (FUNC)p[0][1]; fun(); fun = (FUNC)p[0][2]; fun(); cout << "-----------------" << endl; da = (int*)p[1]; cout << "data1_ = " << da << endl; cout << "-----------------" << endl; cout << "第二个基类的虚函数表如下: " << endl; Base2 b2; p = (long**)&b2; fun = (FUNC)p[0][0]; fun(); fun = (FUNC)p[0][1]; fun(); fun = (FUNC)p[0][2]; fun(); cout << "-----------------" << endl; da = (int*)p[1]; cout << "data2_ = " << da << endl; cout << "-----------------" << endl; cout << "第三个基类的虚函数表如下: " << endl; Base3 b3; p = (long**)&b3; fun = (FUNC)p[0][0]; fun(); fun = (FUNC)p[0][1]; fun(); fun = (FUNC)p[0][2]; fun(); cout << "-----------------" << endl; da = (int*)p[1]; cout << "data3_ = " << da << endl; cout << "-----------------" << endl; cout << "派生类的虚函数表如下: " << endl; Derived d; p = (long**)&d; //定义一个p, 指向虚表指针的指针 fun = (FUNC)p[0][0]; fun(); //函数指针 == 函数地址 == 函数名 ? fun = (FUNC)p[0][1]; fun(); fun = (FUNC)p[0][2]; fun(); fun = (FUNC)p[0][3]; fun(); cout << "-----------------" << endl; // 为什么跳过p[1][n]呢,因为p[1][0]存放的是 data1_,虽然数据成员并没有存在虚表里面,但是派生类因为继承的关系,依然继承了基类里面的数据成员,所以通过指针访问虚表指针进而调用虚函数时,要跳过派生类中继承的基类的数据成员的内存存储空间(位置) // 验证一下: da = (int*)p[1]; cout << "data1_ = " << da << " 成员数据并不存放于虚表中" << endl; cout << "-----------------" << endl; fun = (FUNC)p[2][0]; fun(); fun = (FUNC)p[2][1]; fun(); fun = (FUNC)p[2][2]; fun(); cout << "-----------------" << endl; da = (int*)p[3]; cout << "data2_ = " << da << " 成员数据并不存放于虚表中" << endl; //fun = (FUNC)p[2][3]; //fun(); //报错,说明继承了多个基类的派生类,它里面独有的虚函数,只存在第一个(按声明顺序)继承的基类的虚表中!!! // 为什么跳过p[1][n]呢,因为p[1][0]存放的是 data2_ cout << "-----------------" << endl; fun = (FUNC)p[4][0]; fun(); fun = (FUNC)p[4][1]; fun(); fun = (FUNC)p[4][2]; fun(); cout << "-----------------" << endl; da = (int*)p[5]; cout << "data3_ = " << da << " 成员数据并不存放于虚表中" << endl; cout << "-----------------" << endl; da = (int*)p[6]; cout << "data4_ = " << da << " 成员数据并不存放于虚表中" << endl; //fun = (FUNC)p[4][3]; //fun(); //报错,同理。 return 0;}
程序运行结果:
size of Base = 8size of Derived = 28-----------------第一个基类的虚函数表如下: Base1::Func1()...Base1::Func2()...Base1::Func3()...-----------------data1_ = 0x1-----------------第二个基类的虚函数表如下: Base2::Func1()...Base2::Func2()...Base2::Func3()...-----------------data2_ = 0x2-----------------第三个基类的虚函数表如下: Base3::Func1()...Base3::Func2()...Base3::Func3()...-----------------data3_ = 0x3-----------------派生类的虚函数表如下: Base1::Func1()...Derived::Func2()...Derived::Func3()...Derived::Func4()... 此函数是派生类特有的哦,只存在派生类覆盖第一个基类的虚表里-----------------data1_ = 0x1 成员数据并不存放于虚表中-----------------Base2::Func1()...Derived::Func2()...Derived::Func3()...-----------------data2_ = 0x2 成员数据并不存放于虚表中-----------------Base3::Func1()...Derived::Func2()...Derived::Func3()...-----------------data3_ = 0x3 成员数据并不存放于虚表中-----------------data4_ = 0x4 成员数据并不存放于虚表中Program ended with exit code: 0
- 虚函数的总结
- 虚函数总结
- 虚函数总结收藏
- 虚函数学习总结
- 虚函数总结
- 虚函数表博文总结
- 虚函数总结
- 虚函数小总结
- 虚函数实现总结
- C++虚函数总结
- 虚函数相关内容总结
- 虚函数总结
- 虚函数总结
- 虚函数和多态性---总结
- C++中的虚函数总结
- 虚函数重要性质总结
- 虚函数的一些总结
- 多态 继承 虚函数总结
- 编译预处理(宏定义练兵场)
- Mysql技术内幕InnoDB存储引擎--《九》性能调优
- aapt.exe错误
- JVM 类加载机制详解
- 常见的Android SO加壳(加密)思路
- 虚函数总结
- json问题积累(1)
- rabbitmq(三)
- the source attachment does not contain the source for the file xxx.class无法关联到某个类
- Linux下functions函数讲解
- 基本数据类型数据是放在栈还是常量池?
- c++ primer 读书笔记之第十章 泛型算法
- java基础之面向对象的继承
- XML总结(1)