深入探索C 物件模型之六 執行期語意學

2021-08-02 06:58:41 字數 2540 閱讀 2291

c++最困難的一點就在於:無法從程式原始碼看出程式表示式的複雜度。因為編譯器會在背後給你做很多的工作。對於一些表示式諸如t a = b +c,編譯器可能會建立在執行期過程建立臨時物件,那麼在程式的出口處編譯器就需要安插必要的**來保證建立的臨時物件都得到有效的析構。如果遇到goto、switch等會產生多個邏輯出口的,那麼每個出口處都要安插**。因此一般而言,我們會把object的定義盡可能放置在使用它的那個程式區段附近,減少不必要的物件產生和銷毀操作。

其實上面的一點反映在c++中就是我們可以在函式內部的任意地方定義變數,但是c程式設計師卻習慣而且也只能在函式起始處定義所有變數。

全域性物件

c++程式中所有的global objects都是被放置在程式的data segment中。在編譯時期class object內容為0,但是constructor需要到程式啟用時才會觸發。因此必須對乙個object做靜態初始化。

另外考慮到跨平台移植性的問題,出現了乙個munch策略。

為每乙個需要靜態初始化的檔案都產生乙個_sti()函式,內帶必要的constructor呼叫或者inline expansions。

為每乙個需要靜態記憶體釋放的操作產生_std()函式,內帶必要的destructor呼叫操作或者inline expansions。

在main函式中產生乙個_main()和乙個_exit()函式,其中_main()用於執行之前產生的所有的_sti()函式,而_exit()用於執行之前產生的所有的_std()函式。

具體如下圖所示:

上面提到的編譯器是如何收集乙個程式中所有的_sti()和_std()的呢?這裡不介紹(每家廠商的編譯器實現方法都不盡相同)

區域性靜態物件

對於區域性靜態物件,我們必須要保證它的constructor和destructor都只能施行一次!!!

new和delete運算子

針對如下**

int *pi = new

int(5)

事實上是分兩個步驟完成的:

呼叫函式庫的_new(int size)來分配所需要的記憶體

給配置而來的物件設定初值或者是呼叫class的constructor操作,這些都必須是在分配空間成功之後進行

上面說的是new運算子,而相對的delete運算子就會去做釋放記憶體空間的操作

檢測指標是否為0

如果指標不為0,則釋放指標指向的記憶體空間

對於deletedelete,如果是要釋放陣列的記憶體空間那麼必須使用後者,如果是前者的話編譯器會認為是釋放第乙個元素。很久以前在呼叫delete的時候是需要**設計者自己指明元素的個數的,現在編譯器可以自己查詢,即使程式設計師手動指明元素個數也會被編譯器忽略。

編譯器通過在陣列的最前端增加乙個額外的word,然後把元素書目存放在這個word裡面,來達到delete的時候確定元素個數。

另外乙個要注意的是,如果使用base class的指標指向derived class的陣列的話,那麼在呼叫delete的話仍然會存在問題,因為delete會按照base class的大小來釋放記憶體,因此我們只能迭代走過整個陣列,通過把指標型別強制轉換成derived class型別的。如下:

point *ptr = new point3d[10]

//如下方式會按照point的大小釋放記憶體,但是卻是錯誤的

delete ptr;

//必須如下做

for(int ix=0; ix < elem_count; ++ix)

placement operate new的語意

有乙個預先定義好的過載的new運算子,成為placement operator new,它需要第二個引數,型別是void *。

point2w *ptw =new(arena) point2w;
其中arena指向記憶體中一段空間

placement operator new只是用來分配所要求的空間,但是不呼叫相關物件的建構函式。因此placement operator new可以用來決定objects被放置在**。

考慮如下**:

void foobar()
上面的**在乙個已經存在的object上構造新的object,那麼即使該class object有乙個destructor,該destructor也不會被呼叫的。臨時性物件

在對完整表示式求值過程中,可能會產生臨時性物件,那麼摧毀臨時性物件,就必要時在完整表示式的最後乙個步驟中,這就保證了該記憶體區域中的值已經不在被使用的時候。否則會造成該臨時性物件已經被摧毀釋放掉,但是該內容還會被訪問到,這會導致未定義的錯誤。

如果乙個臨時性物件被繫結於乙個reference,物件將殘留,直到被初始化之refenece的生命結束,或者直到臨時物件的生命範疇結束。

深入探索C 物件模型 之 執行期語意學

在 c 中的一件很困難的事,就是不太容易從程式 看出表示式的複雜度。如下面語句 if yy.operator xx.getvalue 將被擴充套件為下面這樣的 c 偽碼 物件的構造和解構 建構函式一般在物件被構造後呼叫,而析構函式必須被放在每乙個離開點 當時 object 還存活 隻前呼叫。一般而言...

深入c 物件模型之執行期語意學

1.物件的構造與解構 一般而言,constructor 和 destructor的安插都會如你所預期 c 偽碼 point point 一般而言會被安插在這裡 一般而言會被安插在這裡 注意 一般而言我們會將物件放置在使用它的程式附近,這樣做可以節省不必要的物件產生操作和摧毀操作。1 全域性物件 c ...

深入探索C 物件模型之物件

物件 一 在c語言中,資料 和 對資料的處理 函式 分開宣告的,也就是說,語言本身並沒有支援 資料和函式 之間的關聯性。例如,typedef struct point3dpoint3d 而在c 中,座標型別和座標數目都可以引數化 template class point type operator ...