C 的底層機制

2021-04-08 13:31:50 字數 4292 閱讀 2848

c++為我們所提供的各種訪問控制僅僅是在編譯階段給我們的限制,也就是說是編譯器確保了你在完成任務之前的正確行為,如果你的行為不正確,那麼你休想構造出任何可執行程式來。

但如果真正到了產生可執行**階段,無論是c,c++,還是pascal,大家都一樣,你認為c和c++編譯器產生的機器**會有所不同嗎,你認為c++產生的機器**會有訪問限制嗎?那麼你錯了。什麼const,private,統統沒有(const變數或許會放入唯讀資料段),它不會再給你任何的限制,你可以利用一切記憶體修改工具或者是自己寫乙個程式對某一程序空間的某一變數進行修改,不管它在你的印象中是private,還是public,對於此時的你來說都一樣,想怎樣便怎樣。

另外,你也不要為c++所提供的什麼晚期**等機制大呼神奇,它也僅僅是在所產生的**中多加了幾條而已,它遠沒有你想象的那麼智慧型,所有的工作都是編譯器幫你完成,真正到了執行的時候,計算機會完全按照編譯器產生的**一絲不苟的執行。

(以下的反彙編**均來自visial c++ 7.0)

一.讓我們從變數開始-----並非你想象的那麼簡單

你認為上面這段**怎麼樣,我感覺就不很安全,它和指標有相同的隱患。因為它所引用的記憶體區域就不合法。

我個人認為,所謂的引用其實就是一種指標,只不過二者的介面並不相同,引用的介面有一定的限制。指標可以一對多,而引用卻只能一對一,即&refer不能被改變,但卻並不能說一對一就是安全的,只不過危險的係數降低罷了。引用比指標更容易控制。

ok,下面來說說指標,曾經有過彙編經驗的人一定會說,恩,指標的某些地方有些像彙編,尤其是那個「*」,怎麼就那麼像彙編中的「」啊。的確,它也涵蓋了乙個定址的過程。看來指標的確是個比較低階的東西。然而引用卻並不那麼直接,雖然程式設計師用起來方便安全了許多。但是你要清楚,只有你可以擁有引用,編譯器可沒有這個工具,計算機並不認識這個東西。因此,它的底層機制實際上是和指標一樣的。不要相信只有一塊記憶體拷貝,不要認為引用可以為你節省乙個指標的空間,因為這一切不會發生,編譯器還是會把引用解釋為指標。不管你相不相信,請看下面這段**:

int& b=a;

lea eax,[a];

mov dword ptr[b],eax;把a的位址賦給位址為b的一塊記憶體

b=50;

mov eax,dword ptr[b];

mov dword ptr[eax],32h;

int *d=&a;

lea eax,[a];

mov dword ptr[d],eax

*d=60;

mov eax,dword ptr[d]

mov dword ptr[eax],3ch;

以上的**均來自具體的編譯器,怎麼樣,相信了吧,好,讓我再來做乙個或許不怎麼恰當的比擬,你一定編過有關線性表和棧的程式吧,線性表是乙個非常靈活的資料結構,在他上面有許多的操作,然而棧呢,它是乙個限制性操作的線性表,它的底層操作實際上是由線性表操作實現的。就好比stack與vector的關係,因此指標和引用的關係就好比線性表和棧的關係,引用也就是受限的指標,它對外的介面和指標雖然並不一樣,但底層是相同的。

下面再來看看引用的乙個重要用途,作為函式的引數傳遞的時候是怎樣的情形:

void swapr(int &a, int &b);

void swapr(int* a, int *b);

int a=10;

int b=20;

swapr(a, b);

lea eax,[a];

push eax; //把a的位址壓入堆疊

lea ecx,[b];

push ecx;

call swapr;

swapr(&a, &b);

lea eax,[a];

push eax;

lea ecx,[b];

push ecx;

call swapr;

怎麼樣,用引用和指標傳遞引數無論是在效率上還是在空間上都是完全一樣的,如果妄想不傳入位址就修改實參的值,簡直就是天方夜譚,這就說明引用的本質就是指標。畢竟它們的行為都太相似了,如果不是這樣,你還有什麼方法去實現引用嗎?記住,引用只不過是編譯器為你提供的乙個有用且安全的工具,對於機器**可無法表示它,它把指標一對多的缺點去除,禁止了你的不安全的操作。但回到問題的本源,他們沒有任何區別。

三. 虛函式使用技巧

3.1 private的虛函式

考慮下面的例子:

class a

private:

virtual void bar()

};class b: public a

};在這個例子中,雖然bar()在a類中是private的,但是仍然可以出現在派生類中,並仍然可以與public或者protected的虛函式一樣產生多型的效果。並不會因為它是private的,就發生a::foo()不能訪問b::bar()的情況,也不會發生b::bar()對a::bar()的override不起作用的情況。

這種寫法的語意是:a告訴b,你最好override我的bar()函式,但是你不要管它如何使用,也不要自己呼叫這個函式。

3.2 建構函式和析構函式中的虛函式呼叫

乙個類的虛函式在它自己的建構函式和析構函式中被呼叫的時候,它們就變成普通函式了,不「虛」了。也就是說不能在建構函式和析構函式中讓自己「多型」。例如:

class a

// 在這裡,無論如何都是a::foo()被呼叫!

~a() // 同上

virtual void foo();

};class b: public a

;void bar()

如果你希望delete a的時候,會導致b::foo()被呼叫,那麼你就錯了。同樣,在new b的時候,a的建構函式被呼叫,但是在a的建構函式中,被呼叫的是a::foo()而不是b::foo()。

3.3 多繼承中的虛函式 3.4 什麼時候使用虛函式

在你設計乙個基類的時候,如果發現乙個函式需要在派生類裡有不同的表現,那麼它就應該是虛的。從設計的角度講,出現在基類中的虛函式是介面,出現在派生類中的虛函式是介面的具體實現。通過這樣的方法,就可以將物件的行為抽象化。

以設計模式[2]中factory method模式為例,creator的factorymethod()就是虛函式,派生類override這個函式後,產生不同的product類,被產生的product類被基類的anoperation()函式使用。基類的anoperation()函式針對product類進行操作,當然product類一定也有多型(虛函式)。

另外乙個例子就是集合操作,假設你有乙個以a類為基類的類層次,又用了乙個std::vector來儲存這個類層次中不同類的例項指標,那麼你一定希望在對這個集合中的類進行操作的時候,不要把每個指標再cast回到它原來的型別(派生類),而是希望對他們進行同樣的操作。那麼就應該將這個「一樣的操作」宣告為virtual。

現實中,遠不只我舉的這兩個例子,但是大的原則都是我前面說到的「如果發現乙個函式需要在派生類裡有不同的表現,那麼它就應該是虛的」。這句話也可以反過來說:「如果你發現基類提供了虛函式,那麼你最好override它」。

附:c++中的虛函式和純虛函式用法

1.虛函式和純虛函式可以定義在同乙個類(class)中,含有純虛函式的類被稱為抽象類(abstract class),而只含有虛函式的類(class)不能被稱為抽象類(abstract class)。

2.虛函式可以被直接使用,也可以被子類(sub class)過載以後以多型的形式呼叫,而純虛函式必須在子類(sub class)中實現該函式才可以使用,因為純虛函式在基類(base class)

只有宣告而沒有定義。

3.虛函式和純虛函式都可以在子類(sub class)中被過載,以多型的形式被呼叫。

4.虛函式和純虛函式通常存在於抽象基類(abstract base class -abc)之中,被繼承的子類過載,目的是提供乙個統一的介面。

5.虛函式的定義形式:virtual ;純虛函式的定義形式:virtual = 0; 在虛函式和純虛函式的定義中不能有static識別符號,原因很簡單,被static修飾的函式在編譯時候要求前期bind,然而虛函式卻是動態繫結(run-time bind),而且被兩者修飾的函式生命週期(life recycle)也不一樣。

6.如果乙個類中含有純虛函式,那麼任何試圖對該類進行例項化的語句都將導致錯誤的產生,因為抽象基類(abc)是不能被直接呼叫的。必須被子類繼承過載以後,根據要求呼叫其子類的方法。

以下為乙個簡單的虛函式和純虛寒數的使用演示,目的是拋磚引玉!

#include

//father class

class virtualbase

;//sub class

class subvirtual :public virtualbase

;/* instance class and sample */

void main()

C 的底層機制

c 為我們所提供的各種訪問控制僅僅是在編譯階段給我們的限制,也就是說是編譯器確保了你在完成任務之前的正確行為,如果你的行為不正確,那麼你休想構造出任何可執行程式來。但如果真正到了產生可執行 階段,無論是c,c 還是pascal,大家都一樣,你認為c和c 編譯器產生的機器 會有所不同嗎,你 認為c 產...

C 的底層機制

c 的底層機制 c 為我們所提供的各種訪問控制僅僅是在編譯階段給我們的限制,也就是說是編譯器確保了你在完成任務之前的正確行為,如果你的行為不正確,那麼你休想構造出任何可執行程式來。但如果真正到了產生可執行 階段,無論是c,c 還是pascal,大家都一樣,你認為c和c 編譯器產生的機器 會有所不同嗎...

MySQL的底層機制

mysql大致可以分為service層和引擎層。目錄 service層 聯結器查詢快取 分析器優化器 執行器儲存引擎層 它包括聯結器 查詢快取 分析器 優化器 執行器。它涵蓋了mysql的大多數核心服務功能和所有的內建函式,所有跨儲存引擎的功能都在這裡實現。負責跟客戶端建立連線 獲取許可權 維持或者...