天天看點

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單步調試,虛表情況如下:

c++,虛函數,單繼承,多繼承虛表剖析

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的虛表呢?

前面已經說過,先拷貝父類的虛表,然後如果子類存在和父類同名的虛函數,則覆寫上去。

編譯器該怎麼覆寫?

我們先來看看虛表的安排情況:

c++,虛函數,單繼承,多繼承虛表剖析

可以看出。對應記憶體關系如下:

c++,虛函數,單繼承,多繼承虛表剖析

編譯器按照聲明類的前後關系,依次拷貝虛表。

先拷貝:

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/archive/2013/05/05/3061355.html