c++,虚函数,单继承,多继承虚表剖析

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 > 1000

class 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 <iostream.h>
#include <string.h>
//////////////////////////////////////////////////////////////////////
// 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 <iostream.h>
//////////////////////////////////////////////////////////////////////
// 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/p/3061355.html