国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計 > 正文

深度探索C++對象模型(9)

2019-11-17 05:16:19
字體:
供稿:網(wǎng)友
介紹

  當(dāng)編譯一個C++程序時,計算機的內(nèi)存被分成了4個區(qū)域,一個包括程序的代碼,一個包括所有的全局變量,一個是堆棧,還有一個是堆(heap),我們稱堆是自由的內(nèi)存區(qū)域,我們可以通過new和delete把對象放在這個區(qū)域。你可以在任何地方分配和釋放自由存儲區(qū)。但是要注重因為分配在堆中的對象沒有作用域的限制,因此一旦new了它,必須delete它,否則程序?qū)⒈罎ⅲ@便是內(nèi)存泄漏。(C#已經(jīng)通過內(nèi)存托管解決了這一令人頭疼的問題)。C++通過new來分配內(nèi)存,new的參數(shù)是一個表達式,該表達式返回需要分配的內(nèi)存字節(jié)數(shù),這是我以前把握的關(guān)于new的知識,下面看看通過這本書,使我們能夠更進一步的了解到些什么。   這一章主要是說Runtime Semantics執(zhí)行期語義學(xué)。  這是我們平時寫的程序片段:
Matrix identity; //一個全局對象
Main()
{
Matrix m1=identity;
……
return 0;
}  很常見的一個代碼片段,雷神從來沒有考慮過identity如何被構(gòu)造,或者如何被銷毀。因為它肯定在Matrix m1=identity之前就被構(gòu)造出來了,并且在main函數(shù)結(jié)束前被銷毀了。我們不用考慮這些問題,好象C++就應(yīng)該這樣。但這本書是研究C++底層機制的。既然我們在看這本書,說明我們希望了解C++的編譯器又做了那些大量的工作,使得我們可以這樣使用對象。  在C++程序中所有的全局對象都被放在data segment中,假如明確賦值,則對象以該值為初值,否則所配置到內(nèi)存內(nèi)容為0。也就是說,假如我們有以下定義

  Int v1=1024;
  Int v2;

  則v1和v2都被配置于data segment,v1值為1024,v2值為0。(雷神在VC6環(huán)境用MFC編程時中發(fā)現(xiàn)假如int v2;v2的值不為0,而是-8,不知為什么?編譯器造成的?)。  假如有一個全局對象,并且這個對象有構(gòu)造函數(shù)和析構(gòu)函數(shù)的話,它需要靜態(tài)的初始化操作和內(nèi)存釋放工作,C++是一種跨平臺的編程語言,因此它的編譯器需要一種可以移植的靜態(tài)初始化和內(nèi)存釋放的方法。下面便是它的策略。

  1、為每一個需要靜態(tài)初始化的檔案產(chǎn)生一個_sit()函數(shù),內(nèi)帶構(gòu)造函數(shù)或內(nèi)聯(lián)的擴展。
  2、為每一個需要靜態(tài)的內(nèi)存釋放操作的文件中,產(chǎn)生一個_std()函數(shù),內(nèi)帶析構(gòu)函數(shù)或內(nèi)聯(lián)的擴展。
  3、提供一個_main()函數(shù),用來調(diào)用所有的_sti()函數(shù),還有一個exit()函數(shù)調(diào)用所有的_std()函數(shù)。

  侯先生說:

  Sit可以理解成static initialization的縮寫。
  Std可以理解成static deallocation的縮寫。

  那么main函數(shù)會被編譯器變成這樣:
Matrix identity; //一個全局對象
Main()
{
_main();//對所有的全局對象做static initialization動作。
Matrix m1=identity;
……
exit();//對所有的全局對象做static deallocation動作。
}
其中_main()會有一個對identity對象的靜態(tài)初始化的_sti函數(shù),象下面?zhèn)未a這樣:
// matrix_c是文件名編碼_identity表示靜態(tài)對象,這樣能夠保證向執(zhí)行文件提供唯一的識別符號
_sti__matrix_c_identity()
{
identity.Matrix:: Matrix(); //這就是靜態(tài)初始化
}  相應(yīng)的在exit()函數(shù)也會有一個_std_matrix_c_identity(),來進行static deallocation動作。
但是被靜態(tài)初始化的對象有一些缺點,在使用異常時,對象不能被放置在try區(qū)段內(nèi)。還有對象的相依順序引出的復(fù)雜度,因此不建議使用需要靜態(tài)初始化的全局對象。  局部靜態(tài)對象在C++底層機制是如何構(gòu)造和在內(nèi)存中銷毀的呢?

  1、導(dǎo)入一個臨時對象用來保護局部靜態(tài)對象的初始化操作。
  2、第一次處理時,臨時對象為false,于是構(gòu)造函數(shù)被調(diào)用,然后臨時對象被改為true.
  3、臨時對象的true或者false便成為了判定對象是否被構(gòu)造的標(biāo)準(zhǔn)。
  4、根據(jù)判定的結(jié)果決定對象的析構(gòu)函數(shù)是否執(zhí)行。  假如一個類定義了構(gòu)造函數(shù)或者析構(gòu)函數(shù),則當(dāng)你定義了一個對象數(shù)組時,編譯器會通過運行庫將你的定義進行加工,例如:
point knots[10]; //我們的定義
vec_new(&knots,sizeof(point),10,&point::point,0); //編譯器調(diào)用vec_new()操作。  下面給出vec_new()原型,不同的編譯器會有差別。
void * vec_new(
void *array, //數(shù)組的起始地址
size_t elem_size, //每個對象的大小
int elem_count, //數(shù)組元素個數(shù)
void(*constrUCtor)(void*),
void(*destructor)(void* ,char)
)
對于明顯獲得初值的元素,vec_new()不再有必要,例如:
point knots[10]={
Point(), //knots[0]
Point(1.0,1.0,0.5), //knots[1]
-1.0 //knots[2]
};
會被編譯器轉(zhuǎn)換成:
//C++偽碼
Point::Point(&knots[0]);
Point::Point(&knots[1],1.0,1.0,0.5);
Point::Point(&knots[2],-1.0,0.0,0.0);
vec_new(&knots,sizeof(point),10,&point::point,0); //剩下的元素,編譯器調(diào)用vec_new()操作。
  怎么樣,很神奇吧。  當(dāng)編譯一個C++程序時,計算機的內(nèi)存被分成了4個區(qū)域,一個包括程序的代碼,一個包括所有的全局變量,一個是堆棧,還有一個是堆(heap),我們稱堆是自由的內(nèi)存區(qū)域,我們可以通過new和delete把對象放在這個區(qū)域。你可以在任何地方分配和釋放自由存儲區(qū)。但是要注重因為分配在堆中的對象沒有作用域的限制,因此一旦new了它,必須delete它,否則程序?qū)⒈罎ⅲ@便是內(nèi)存泄漏。(C#已經(jīng)通過內(nèi)存托管解決了這一令人頭疼的問題)。C++通過new來分配內(nèi)存,new的參數(shù)是一個表達式,該表達式返回需要分配的內(nèi)存字節(jié)數(shù),這是我以前把握的關(guān)于new的知識,下面看看通過這本書,使我們能夠更進一步的了解到些什么。

  Point3d *origin=new Point3d; //我們new 了一個Point3d對象

  編譯器開始工作,上面的一行代碼被轉(zhuǎn)換成為下面的偽碼:
Point3d * origin;
If(origin=_new(sizeof(Point3d)))
{
try{
origin=Point3d::Point3d(origin);
}
catch(…){
_delete(origin);
throw;
}
}  而delete origin;

  會被轉(zhuǎn)換成(雷神將書上的代碼改為exception handling情況):
if(origin!=0){
try{
Point3d::~Point3d(origin);
_delete(origin);
catch(…){
_delete(origin); //不知對否?
throw;
}
}  一般來說對于new的操作都直截了當(dāng),但語言要求每一次對new的調(diào)用都必須傳回一個唯一的指針,解決這個問題的辦法是,傳回一個指針指向一個默認為size=1的內(nèi)存區(qū)塊,實際上是以標(biāo)準(zhǔn)的C的malloc()來完成。同樣delete也是由標(biāo)準(zhǔn)C的free()來完成。原來如此。  最后這篇筆記再說說臨時對象的問題。

  T Operator+(const T&,const T&); //假如我們有一個函數(shù)

  T a,b,c; //以及三個對象:

  c=a+b;

  //可能會導(dǎo)致臨時對象產(chǎn)生。用來放置a+b的返回值。然后再由 T的copy constructor把臨時對象當(dāng)作c的初值。也有可能直接由拷貝構(gòu)造將a+b的值放到c中,這時便不需要臨時對象。另外還有一種可能通過操作符的重載定義,經(jīng)named return value優(yōu)化也可以獲得c對象。這三種方法結(jié)果一樣,區(qū)別在于初始化的成本。對臨時對象書上有很好的總結(jié):

  在某些環(huán)境下,有PRocessor產(chǎn)生的臨時對象是有必要的,也是比較方便的,這樣的臨時對象由編譯器決定。

  臨時對象的銷毀應(yīng)該是對完整表達式求值過程的最后一個步驟。

  因為臨時對象是根據(jù)執(zhí)行期語義有條件的產(chǎn)生,因此它的生命規(guī)則就顯得很復(fù)雜。C++標(biāo)準(zhǔn)要求凡含有表達式執(zhí)行結(jié)果的臨時對象,應(yīng)該保留到對象的初始化操作完成為止。當(dāng)然這樣也會有例外,當(dāng)一個臨時對象被一個引用綁定時,對象將殘留,直到被初始化的引用的生命結(jié)束,或者超出臨時對象的作用域。  好了今天很有收獲,馬上就會結(jié)束這本書的學(xué)習(xí)了。下一章的標(biāo)題 站在對象模型的尖端 我有些迫不及待了。

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 务川| 勃利县| 堆龙德庆县| 乌拉特中旗| 阳泉市| 宁津县| 利辛县| 资兴市| 巴青县| 定州市| 大荔县| 兴宁市| 内江市| 永善县| 炉霍县| 筠连县| 宿迁市| 泰和县| 新竹县| 平塘县| 哈尔滨市| 家居| 桂阳县| 鹤峰县| 吴旗县| 永平县| 通渭县| 连山| 库伦旗| 吉林市| 巴林左旗| 北宁市| 馆陶县| 伊吾县| 巫溪县| 准格尔旗| 广德县| 新巴尔虎右旗| 洛浦县| 旬阳县| 台山市|