概述面向對象語言的重要概念和實現技術
以C++語言為例,介紹如何將C++程序翻譯成C程序
實際的編譯器大都把C++程序直接翻譯成低級語言程序
編譯器對于繼承的處理,往往是父類包含子類的對象,例如
struct Base
{
int a;
};
struct Derived
{
Base base;
int b;
};
再深入的偶也不會了.........
下面我們討論對面向對象多態特性的處理。多態是面向對象語言最為精彩的地方,可以說是誕生無數神奇的特性,多態給了我們極大的自由,讓我們可以在一套類的體系結構中自由游走,可以寫很少的代碼,但是完成復雜的功能,可以在別人的基礎上做二次開發......
好了,不打廣告了,下面舉一個簡單的例子說明編譯器對多態的處理。首先介紹一下虛函數表。大多數編譯器實現多態都是通過虛函數表,虛函數表是一個保存了多個函數指針的表,其中每個函數對應著一個虛函數(非虛函數不參與),調用一個虛函數的時候,編譯器通過虛函數表查找相應位置的函數,所以編譯時編譯器不知道這個函數會調用到基類的函數還是派生類的函數,所以叫動態綁定或運行時綁定。
以下是一個簡單的多態實例程序,有了虛函數所以可以在基類指針指向派生類對象的時候運行派生類的函數。
#include <iostream>
using namespace std;
class Base
{
public:
virtual void Hello();
};
void Base::Hello()
{
cout<<"Hello in Base"<<endl;
}
class Derived:public Base
{
public:
void Hello();
};
void Derived::Hello()
{
cout<<"Hello in Derived"<<endl;
}
int main()
{
Base* p=new Base;
p->Hello();//Hello in Base
p=new Derived;
p->Hello();//Hello in Derived
return 0;
}
---------------------------------------我是華麗的分割線------------------------------------
以下是對應的翻譯結果,但是需要注意的是這僅僅是一個示例,首先pFun這個函數指針無法指向所有類型的函數,而且按照上篇文章所述,翻譯Hello()函數的時候至少應該在參數里加一個本身的引用的....深入的我也不會了.....
#include <iostream>
using namespace std;
typedef void (*pFun)();//定義函數指針
void Base_Hello()//基類虛函數
{
cout<<"Hello in Base"<<endl;
}
pFun Base_vtf[1]={Base_Hello};//Base的虛函數表,編譯器自動把所有virtual的函數生成一個虛函數表
struct Base
{
Base(){vft=Base_vtf;}
pFun* vft;//虛函數表指針
};
void Derived_Hello()//父類覆蓋基類的函數
{
cout<<"Hello in Derived"<<endl;
}
pFun Derived_vtf[1]={Derived_Hello};//Derived的虛函數表
//編譯器會首先復制基類的虛函數表,然后把被覆蓋的虛函數用父類的虛函數代替
//然后加入父類的虛函數
struct Derived
{
Derived(){vft=Derived_vtf;}
pFun* vft;
};
int main()
{
//編譯器會把非虛函數的引用直接翻譯成對應的函數調用
//而虛函數則按照虛函數表取相應的函數,這樣如果基類指針指向派生類
//那么派生類的虛函數表中相應位置已經被派生類的方法覆蓋了,從而可以調用到派生類的方法
Base* p=new Base;
p->vft[0]();//Hello in Base
p=(Base*)new Derived;
p->vft[0]();//Hello in Derived
return 0;
}
從上面我們可以大致了解多態的處理過程和虛函數表的作用。我們可以用VC驗證一下虛函數表。
設我們有這樣的一個類:
class Base {
public:
virtual void f() { cout << "Base::f" << endl; }
virtual void g() { cout << "Base::g" << endl; }
virtual void h() { cout << "Base::h" << endl; }
};
按照上面的說法,我們可以通過Base的實例來得到虛函數表。 下面是實際例程:
typedef void(*Fun)(void);
Base b;
Fun pFun = NULL;
cout << "虛函數表地址:" << (int*)(&b) << endl;
cout << "虛函數表 — 第一個函數地址:" << (int*)*(int*)(&b) << endl;
// Invoke the first virtual function
pFun = (Fun)*((int*)*(int*)(&b));
pFun();
實際運行經果如下:(Windows XP+VS2003, Linux 2.6.22 + GCC 4.1.3)
虛函數表地址:0012FED4
虛函數表 — 第一個函數地址:0044F148
Base::f
通過這個示例,我們可以看到,我們可以通過強行把&b轉成int *,取得虛函數表的地址,然后,再次取址就可以得到第一個虛函數的地址了,也就是Base::f(),這在上面的程序中得到了驗證(把int* 強制轉成了函數指針)。通過這個示例,我們就可以知道如果要調用Base::g()和Base::h(),其代碼如下:
(Fun)*((int*)*(int*)(&b)+0); // Base::f()
(Fun)*((int*)*(int*)(&b)+1); // Base::g()
(Fun)*((int*)*(int*)(&b)+2); // Base::h()
以上引用自:http://www.cppblog.com/xczhang/archive/2008/01/20/41508.html非常感謝作者
下面這篇文章是使用VCdebug窗口觀察虛函數的,也很不錯,一并感謝作者
http://www.cnblogs.com/wirelesser/archive/2008/03/09/1097463.html
總結:本文針對面向對象的多態特性和繼承特性,介紹了面向對象語言的編譯過程。我們可以得到如下啟示:
多態是通過虛函數表實現的,實際如果我們用sizeof看一個類的長度的話,會發現該長度實際由所有非靜態成員和虛函數表(四個字節的指針)構成.
由此我們對多態有了更深刻的理解.
文章出處:http://www.cnblogs.com/sdqxcxh/ 作者:布拉德比特
上一篇 微博盈利的方向應該是增值服務