博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
c++,虚函数,单继承,多继承虚表剖析
阅读量:4948 次
发布时间:2019-06-11

本文共 4665 字,大约阅读时间需要 15 分钟。

C++的多态性体现在两个方面,一个函数重载,一个虚函数,重载的多态性是在编译器编译期的时候早已经决定了。编译器实现函数重载的时候,也就是进行了名称粉碎。

而虚函数则是运行期的多态。多态有什么用? 可能你会有此疑惑。最普遍的说法是“提高代码的重用性”。

如果大家对逆向感兴趣的话,虚函数的内存结构那是必须得掌握的,下面我们来慢慢剖析类中虚函数的内存结构。

class Ca{public:  Ca()  {  }  virtual ~Ca()  {  }  virtual void Fun1()  {  }  virtual void Fun2()  {  }  void Fun3()  {  }};class Cb : public Ca{public:  virtual void Fun1()  {  }};

现在我们讨论单继承的虚表情况。首先我们定义2个对象:

int main(int argc, char* argv[]){  Ca theA;  Cb theB;   return 0;}

F10单步调试,虚表情况如下:

Ca虚表如下:

Ca::~Ca   Ca::Fun1  Ca::Fun2

Cb的虚表呢?

首先拷贝一份父类的虚表,然后在把自己的与父类同名的虚函数覆盖上去,则是子类的虚表

Ca::~Ca   Ca::Fun1  Ca::Fun2

最后Cb的虚表如下:

Cb::~Cb   Cb::Fun1  Ca::Fun2

前提是子类必须有同名虚函数,则覆盖上去,这也就是为什么叫覆盖,而不叫隐藏的道理。

下面我们在来剖析多继承的虚表会是什么样的情况。

class Ca{public:  Ca()  {  }  virtual ~Ca()  {  }  virtual void Fun1()  {  }  virtual void Fun2()  {  }};class Cb{public:  virtual void Fun1()  {  }  virtual void Fun2()  {  }};class Cc : public Ca, public Cb{public:  virtual void Fun1()  {  }};
int main(int argc, char* argv[]){  Ca theA;  Cb theB;  Cc theC;  theC.Fun1();  return 0;}

 

看了前面单继承的剖析,我们来写出Ca和Cb的虚表

Ca::~Ca  Ca:Fun1  Ca::Fun2

Cb::Fun1  Ca::Fun2

 

Cc的虚表呢?

前面已经说过,先拷贝父类的虚表,然后如果子类存在和父类同名的虚函数,则覆盖上去。

编译器该怎么覆盖?

我们先来看看虚表的安排情况:

可以看出。对应内存关系如下:

编译器按照声明类的前后关系,依次拷贝虚表。

先拷贝:

Ca::~Ca  Ca:Fun1  Ca::Fun2

在依次在拷贝Cb。

Cb::Fun1  Ca::Fun2

然后。由于Cc由Fun1和~Cc虚函数,产生覆盖。

最后。覆盖后的Cc的虚表如下:

Cc::~Cc  Cc:Fun1  Ca::Fun2

Cc::Fun1  Ca::Fun2

如果对虚表的结构了如执掌了。那么我们就可以通过数组下标的方式访问虚表,就可以突破编译器的限制! 当然不建议这么做!

现在,我们来实战一把。

题目如下:

“不能使用virtual关键字 ,模拟虚函数来表现出多态性:

写一基类Person 有sayHello,sayGoodbye函数

有一子类student 它也有自己的sayHello, sayGoodbye函数

请在这两个类里加入函数 vsayHello, vsayGoodbye函数

来表现出对象的多态性(分别调用自己的对应的sayHello和sayGoodbye)“

附带源码:

// Person.h: interface for the CPerson class.////#if !defined(AFX_PERSON_H__F2DA095E_4153_409D_B7B0_BBEBCF4B9B63__INCLUDED_)#define AFX_PERSON_H__F2DA095E_4153_409D_B7B0_BBEBCF4B9B63__INCLUDED_#if _MSC_VER > 1000#pragma once#endif // _MSC_VER > 1000class CPerson;typedef void (CPerson::*CPERSON_PFUN)();class CPerson  {public:  void vsayGoodbye();  void vsayHello();    void sayGoodbye();    void sayHello();    CPerson();  CPerson(const int* pFun);    ~CPerson();protected:  static CPERSON_PFUN m_g_cPerpFun[2];  CPERSON_PFUN m_cpFun[2];};#endif // !defined(AFX_PERSON_H__F2DA095E_4153_409D_B7B0_BBEBCF4B9B63__INCLUDED_)
// Person.cpp: implementation of the CPerson class.////#include "stdafx.h"#include "Person.h"#include 
#include
//// Construction/Destruction//CPERSON_PFUN CPerson::m_g_cPerpFun[2] = {CPerson::sayHello, CPerson::sayGoodbye};CPerson::CPerson(){ // 模拟虚表赋值 memcpy(m_cpFun, m_g_cPerpFun, sizeof(m_cpFun));}// 派生类构造前先构造父类虚表CPerson::CPerson(const int* pFun){ memcpy(m_cpFun, pFun, sizeof(m_cpFun));}CPerson::~CPerson(){ // 模拟还原虚表 memcpy(m_cpFun, m_g_cPerpFun, sizeof(m_cpFun));}void CPerson::sayHello(){ cout << "CPerson::sayHello()" << endl;}void CPerson::sayGoodbye(){ cout << "CPerson::sayGoodbye()" << endl;}void CPerson::vsayHello(){ (this->*(m_cpFun[0]))();}void CPerson::vsayGoodbye(){ (this->*(m_cpFun[1]))();}
// Student.h: interface for the CStudent class.////#if !defined(AFX_STUDENT_H__4C004418_D79D_4809_900D_675FADEB905C__INCLUDED_)#define AFX_STUDENT_H__4C004418_D79D_4809_900D_675FADEB905C__INCLUDED_#if _MSC_VER > 1000#pragma once#endif // _MSC_VER > 1000#include "Person.h"class CStudent;typedef void (CStudent::*STUDENT_PFUN)();class CStudent : public CPerson  {public:  void vsayGoodbye();    void vsayHello();  void sayGoodbye();    void sayHello();    CStudent();  // VC6.0测试:  // 发现编译器一个BUG,在析构函数前写上virtual关键字,则报错,会  // 导致m_g_cStudentpFun成员数组在分配总空间上为16个字节。  // Visual Stodio2005加和不加virtual关键字都测试正常.    ~CStudent();private:  static STUDENT_PFUN m_g_cStudentpFun[2];};#endif // !defined(AFX_STUDENT_H__4C004418_D79D_4809_900D_675FADEB905C__INCLUDED_)
// Student.cpp: implementation of the CStudent class.////#include "stdafx.h"#include "Student.h"#include 
//// Construction/Destruction//// 模拟编译器分配虚表信息STUDENT_PFUN CStudent::m_g_cStudentpFun[2] = {CStudent::sayHello, CStudent::sayGoodbye};CStudent::CStudent() : CPerson((int*)CStudent::m_g_cStudentpFun){ }CStudent::~CStudent(){}void CStudent::sayHello(){ cout << "CStudent::sayHello()" << endl;}void CStudent::sayGoodbye(){ cout << "CStudent::sayGoodbye()" << endl;}void CStudent::vsayHello(){ (this->*(m_cpFun[0]))();}void CStudent::vsayGoodbye(){ (this->*(m_cpFun[1]))();}
// Work2.cpp : Defines the entry point for the console application.//#include "stdafx.h"#include "Person.h"#include "Student.h"int main(int argc, char* argv[]){  CStudent theStu;  CPerson* pObj = &theStu;  // 多态性  pObj->vsayHello();  pObj->vsayGoodbye();  return 0;}

 

转载于:https://www.cnblogs.com/ziolo/archive/2013/05/05/3061355.html

你可能感兴趣的文章
SwaggerUI+SpringMVC——构建RestFul API的可视化界面
查看>>
springmvc怎么在启动时自己执行一个线程
查看>>
流操作的规律
查看>>
Python基础学习15--异常的分类与处理
查看>>
javascript运算符的优先级
查看>>
React + Redux 入门(一):抛开 React 学 Redux
查看>>
13位时间戳和时间格式化转换,工具类
查看>>
vue router-link子级返回父级页面
查看>>
C# 通知机制 IObserver<T> 和 IObservable<T>
查看>>
Code of Conduct by jsFoundation
查看>>
div 只显示两行超出部分隐藏
查看>>
C#小练习ⅲ
查看>>
debounce、throttle、requestAnimationFrame
查看>>
linux下的C语言快速学习—进程和文件
查看>>
电源防反接保护电路
查看>>
stm32 堆和栈(stm32 Heap & Stack)
查看>>
SpringMVC从入门到精通之第三章
查看>>
JS基础-dom操作
查看>>
【转】Android详细的对话框AlertDialog.Builder使用方法
查看>>
Unite Beijing 2015大型活动
查看>>