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