版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)
文檔簡介
1、C+ Primer(全部) 深入 C+對象模型(前四章)STL 源碼剖析(前四章)C 和指針 C 陷阱和缺陷(全部) 321C+C+編譯過程高級語言程序編譯的過程:預(yù)處理、編譯、匯編、1.1。預(yù)處理:頭文件的包含、宏定義的擴展、條件編譯的選擇編譯:把源代碼翻譯成中間語言,即匯編語言匯編:把匯編代碼翻譯成機器代碼,即目標(biāo)代碼:找到所要用到函數(shù)所在的目標(biāo)文件,并頭文件在一起為可執(zhí)行文件1.2頭文件中的編譯宏” #ifndef INCvxWorksh、#define INCvxWorksh、#endif” 的作用是防止該頭文件被重復(fù)#include filename.h 與。#include 不同?
2、#include 在系統(tǒng)定義的頭文件路徑內(nèi)(包括命令行中用參數(shù)設(shè)置的包含路徑)搜索頭文件;#include filename.h在當(dāng)前路徑內(nèi)搜索頭文件。extern 關(guān)鍵字1.3extern 變量。作用是變量和函數(shù)為外部,即該變量或函數(shù)名在其它文件中可見。externx;只不定義,但是如果初始化的話就是定義了,比如 externy= 3。externx,y;告訴編譯器其后的變量已經(jīng)在別的文件中說明,不再為它們分配內(nèi)存。 (可以多處,但是定義只能有一處)通常,在模塊的頭文件中對本模塊提供給其它模塊的函數(shù)和全局變量以關(guān)鍵字 extern。(沒 extern,想用其他頭文件的全局變量全局函數(shù)就得 i
3、nclude 那個頭文件;不想 inlcude 的話就得在本文件內(nèi)使用 extern一下這個變量已經(jīng)在其他地方定義了)真理:只在頭文件中做而不定義舉例:test1.h全局變量 strvoid fun1();extern char str; /test1.cpp#include”test1.h” char str = “123456”;/定義全局變量 str void fun1()coutstrendl;test2.cpp#include”test1.h” void fun2() coutstrendl; test2.cpp 只要包含了 test1.h 文件就可以使用 str。但是如果把 str
4、 的定義放在頭文件 test1.h 的話,這里 test1.cpp 和 test2.cpp 都包含了test1.h 在編譯的時候會導(dǎo)致多沖定義錯誤。換案:把 str 的定義從 test1.cpp 挪到 test1.h 中: extern char str = “123456”然后在 test2.cpp 中去掉#include “test1.h“ 然后加上extern char str;這樣 fun2 同樣可以運行,以及不會編譯錯誤。但是這樣的話,test1.h 中的 fun1 函數(shù)就無法使用了! extern 函數(shù) 同上extern C 在 C+環(huán)境下使用 C 函數(shù)的時候,常常會出現(xiàn)編譯器無法
5、找到 obj 模塊中的C 函數(shù)定義,從而導(dǎo)致失敗的情況。x,y ); C+編譯器會產(chǎn)生像_foo_之類的名字,實現(xiàn)函數(shù)重載,解void foo(決了函數(shù)的多態(tài)問題。而 C 語言不會,C 編譯器編譯后在符號庫中的名字為_foo。因此會造成時找不到對應(yīng)函數(shù)的情況。extern C修飾的變量和函數(shù)是按照 C 語言方式編譯和連接的。extern C的目的是實現(xiàn) C+與 C 及其它語言的混合編程。C+調(diào)用 C 模塊C 語言頭文件 cExle.h externadd(x,y);le.hC 語言實現(xiàn)文件 cExle.c #include cExadd(x,y )C+實現(xiàn)文件 cppFile.cpp exte
6、rn C #include cExC 調(diào)用 C+模塊le.hmain()add(2,3);C+頭文件 cppExle.h extern Cadd(x,y );le.hC+實現(xiàn)文件 cppExle.cpp #include cppExadd(x,y)C 實現(xiàn)文件 cFile.c extern注意:C 語言中不能直接中定義的 extern C函數(shù)add(x,y );main()add(2,3);了 extern C的該頭文件,應(yīng)該僅在 C 文件中將 C+為 extern 類型。n; 另一個文件定義 long n;編譯器不一定能報錯。PS:一個文件extern運行時會發(fā)生的情況:1、編譯出錯 2、l
7、ong 和起使用 4、出錯了sic 關(guān)鍵字長短一樣 3、兩個類型恰好可以一1.4sic 修飾的變量和函數(shù)都只限定于本文件使用。sic 修飾的全局變量與定義同時進行。Extern 和 sic 不能同時修飾一個變量。靜態(tài)函數(shù)會被自動分配在一個一直使用的區(qū),直到退出應(yīng)用程序?qū)嵗?,避免了調(diào)用函數(shù)時壓棧出棧,速度快很多。1、 sic 變量1).局部 a.靜態(tài)局部變量在函數(shù)內(nèi)定義,生存期為整個源程序,但作用域與自動變量相同,只能在定義該變量的函數(shù)內(nèi)使用。退出該函數(shù)后,盡管該變量還繼續(xù)存在,但不能使用它。b.對基本類型的靜態(tài)局部變量若在說明時未賦以初值,則系統(tǒng)自動賦予 0 值。而對自動變量不賦初值,則其值是
8、不定的。2).全局全局變量本身就是靜態(tài)方式, 靜態(tài)全局變量當(dāng)然也是靜態(tài)方式。但是他們的作用域,非靜態(tài)全局 變量的作用域是整個源程序(多個源文件可以共同使用); 而靜態(tài)全局變量則限制了其作用域, 即只在定義該變量的源文件內(nèi)有效, 在同一源程序的其它源文件中不能使用它。2、 sic 函數(shù)(也叫函數(shù))只能被本文件中的函數(shù)調(diào)用,而不能被同一程序其它文件中的函數(shù)調(diào)用。區(qū)別于一般的非靜態(tài)函數(shù)(外部函數(shù))Const 關(guān)鍵字全局作用域定義非 const 變量,默認(rèn)為 extern,整個程序都可以問,但是又不想 include 該文件的話,就要加一個 extern 該變量)1.5(別的文件想要訪但是全局作用域定
9、義的 const 變量,具有 sic 特性,則只有本文件可以使用。但是const 可以與extern 連用來該常量可以作用于其他編譯模塊。F 1.cFile2.cextern constextern constt = 4;t;/這樣之后,后面才能使用 t 變量但是如果 file2.c 在 file1.c 之前編譯的話,就出錯了!定義一個宏,比較兩個數(shù) a、b 的大小,不能使用大于、小于、if 語句#define max(a,b) (a)-(b)&(1 ()自左向右優(yōu)先級1.8單目次之:! + - - (type) * & sizeof自右向左 比方說*p()的意思是*(p()雙目再次之:算術(shù)、
10、移位、關(guān)系、邏輯 從左到右三目:條件運算符,右到左賦值運算符再再再次之:(從右到左)最低的就是逗號了,從左到右注:=和!=優(yōu)先級要低于其他關(guān)系運算符的優(yōu)先級 ab = cd以及邏輯運算符優(yōu)先級中 &高于高于|高于&高于| C+內(nèi)存管理代碼段:保存代碼堆棧段數(shù)據(jù)段:保存全局?jǐn)?shù)據(jù)和靜態(tài)數(shù)據(jù)1.9char str1 = abc; char str2 = abc;const char str3 = abc; const char str4 = abc; const char* str5 = abc; const char* str6 = abc; cout boolalpha ( str1=str2
11、) endl; / false cout boolalpha ( str3=str4 ) endl; / false cout boolalpha ( str5=str6 ) 數(shù)組 ajp+-指向數(shù)組 a0=數(shù)組 a1pi指針數(shù)組 aipijpi偏移 j所指元素數(shù)組元素 aij*p指針 p0數(shù)組 a0*p指針 p0所指元素數(shù)組元素 a00main 函數(shù)的 EBX,ESI,EDI 出棧ESP 指向 EBP 所指的內(nèi)存單元main 函數(shù) EBP 值出棧程序跳轉(zhuǎn)到函數(shù)調(diào)用結(jié)束后需要繼續(xù)執(zhí)行的指令地址棧頂指針 ESP 向下移動(跳過 f 函數(shù)入棧參數(shù))f 函數(shù)參數(shù)出棧inline 函數(shù)目的:解決程序中
12、函數(shù)調(diào)用的效率問題。使用:inline 函數(shù)適用于函數(shù)體代碼短,但是頻繁調(diào)用的函數(shù)。實現(xiàn):程序編譯時,編譯器將程序中出現(xiàn)的內(nèi)聯(lián)函數(shù)的調(diào)用表達式用內(nèi)聯(lián)函數(shù)的函數(shù)體來進行替換。缺點:增加目標(biāo)程序代碼量,進而增加空間開銷,優(yōu)點:減少因為函數(shù)調(diào)用引起的開銷,主要是參數(shù)壓棧、棧幀開辟與回收,以及寄存器保存與恢復(fù)等。注意:在內(nèi)聯(lián)函數(shù)內(nèi)不允許用循環(huán)語句和開關(guān)語句。所有對虛函數(shù)的調(diào)用都會使inlining 落空。編譯器通常不對通過指針而進行的調(diào)用實施 inlining malloc 和 new1.211.22memmove、memcpy、memset、strcpy 和 mem1.23ymemcpy 和 mem
13、move 都是將源地址的若干個字符拷貝到目標(biāo)地址。如果源地址和目標(biāo)地址有,則 memcpy 不能保證拷貝正確,但 memmove 可以保證拷貝正確。void* memcpy( void *dest, const void *src, size_t count)char* pdest = sic_cast( dest );const char* psrc = sic_cast( src );assert(dest!=NULL & src!=NULL);assert(pdest = psrc+count | psrc = pdest+count); while(count-) 0)mallocne
14、w返回值返回 void*需要類型轉(zhuǎn)換返回指針帶有類型信息參數(shù)需要指定分配空間大小無需指定分配空間大小初始化不初始化調(diào)用構(gòu)造函數(shù)*pdest+ = *psrc;return dest;void* memmove( void *dest, const void *src, size_t count)if(count=0)return dst;char* pdest = sic_cast( dest );const char* psrc = sic_cast( src );if(dest=NULL & src=NULL) return dest;if(pdest = psrc+count | pde
15、st 0)*pdest+ = *psrc+;elsepdest += count; psrc += count;for(i=0;icount;i+)*(-pdest) = *(-psrc);return dest;void* memset( void *dest,val, size_t count)char* pdest = sic_cast( dest );assert(dest!=NULL); while(count-) 0)*pdest+ = (char)val;return dest;char* strcpy( char *dest, const char *src)i =0;asse
16、rt(dest!=NULL & src!=NULL); while( (*(dest+i)=*(src+i) != 0 )i+;return dest;C+隱式轉(zhuǎn)換C+隱式轉(zhuǎn)換發(fā)生條件:在混合類型表達式中,操作數(shù)被轉(zhuǎn)換成相同的類型用作 if 語句或循環(huán)語句的條件時,被轉(zhuǎn)換為 bool 類型用于 switch 語句時,轉(zhuǎn)為整數(shù)類型用來初始化某個變量(包括函數(shù)實參、return 語句),轉(zhuǎn)為變量的類型類隱式轉(zhuǎn)換:class Fruitstring name;/定義一個 name 成員string colour;/定義一個 colour 成員public:Fruit(const string &ns
17、t,const string &cst = green):name(nst),colour(cst)/構(gòu)造函數(shù) bool isSame(const Fruit &otherFruit)main()Fruit apple(apple);coutapple = /apple/ ?:apple.isSame(string(apple);可以用單個實參來調(diào)用的構(gòu)造函數(shù)定義了從形參類型到該類型的一個隱式轉(zhuǎn)換。 String(“apple”)隱式轉(zhuǎn)換為 Fruit。在構(gòu)造函數(shù)前面加 explicit 關(guān)鍵字,再隱式轉(zhuǎn)換就會導(dǎo)致編譯失敗。類隱式反向轉(zhuǎn)換:Class Fontpublic:1.24operat
18、or Fon private:ndle() const return f;/隱式轉(zhuǎn)換函數(shù)Fonndle f;void chageFontSize(Fonndle f,newSize)Font f;w;changeFontSize(f, w);/f 隱式從 Font 轉(zhuǎn)換為 FonndleC+強制類型轉(zhuǎn)換(盡量少做動作)1.25C 風(fēng)格(C-style)強制語法:(T) exdivs/ cast exdivsto be of type T函數(shù)風(fēng)格(Function-style)強制語法:T(exdivs) / cast exdivsto be of type T。兩種形式之間沒有本質(zhì)上的不同,稱
19、為舊風(fēng)格(old-style)的強制(1) sic_cast ( expres)用來強迫隱式轉(zhuǎn)換,而且返回的是個副本!把 expres轉(zhuǎn)換為 type-id 類型,expres和 type-id 必須是指針、算術(shù)類型或枚舉類型。,但沒有運行時類型檢查來保證轉(zhuǎn)換的安全性。sic_cast ( expres)用法:(1) 類層次結(jié)構(gòu)中基類類之間指針或的轉(zhuǎn)換。進行上行轉(zhuǎn)換(把子類的指針或轉(zhuǎn)換成基類表示)是安全的;進行下行轉(zhuǎn)換(把基類指針或轉(zhuǎn)換成子類表示)時,由于沒有動態(tài)類型檢查,所以是不安全的。double *d = sic_cast(&n) /無關(guān)類型指針轉(zhuǎn)換,編譯錯誤(2) 基本數(shù)據(jù)類型之間的轉(zhuǎn)
20、換,如把轉(zhuǎn)換成 char,把轉(zhuǎn)換成 enum。n = 6; double d = sic_cast(n);把空指針轉(zhuǎn)換成目標(biāo)類型的空指針。把任何類型的表達式轉(zhuǎn)換成 void 類型。void *p = sic_cast(pn);(2)dynamic_cast ( expres)主要用來執(zhí)行“安全向下“。把 expres轉(zhuǎn)換成 type-id 類型的對象。type-id必須是類的指針、類的或者void *;如果 type-id 是類指針類型,那么 expres也必須是一個指針,如果 type-id 是一個,那么 expres也必須是一個。dynamic_cast 主要用于類層次間的上行轉(zhuǎn)換和下行轉(zhuǎn)
21、換,還可以用于類之間的交叉轉(zhuǎn)換。如果 expres類型是 type-id 的基類,運行時檢查是否expres實際是一個指向 type-id 類型的完整對象,如果是,結(jié)果返回指向 type-id 類型的完整對象,否則返回 NULL。BaseClass *pb = dynamic_cast(pd); /子類-父類,正確DerivedClass *pd2=dynamic_cast(pb2);/父類-子類,結(jié)果 NULL(3)const_cast (expres)用來修改類型的 const 或 volatile 屬性。除了 const 或volatile 修飾之外,type_id和 exdivs的類型
22、是一樣的。常量指針被轉(zhuǎn)化成非常量指針,并且仍然指向原來的對象;常量被轉(zhuǎn)換成非常量,并且仍然指向原來的對象;常量對象被轉(zhuǎn)換成非常量對象。const SA ra; /ra.i = 10; /直接修改 const 類型,編譯錯誤SA &rb = const_cast(ra); rb.i = 10;比方說有些函數(shù)對其參數(shù)只讀不寫,但卻只接受非 const 指針變量!reinpreter_cast(expres)(4)ret_cast 用于底層的強制,實際動作可能取決于編譯器。例如,將一re個指針指針、 re為一個整數(shù),也可以把一個整數(shù)轉(zhuǎn)換成一個指針。type-id 必須是一個、算術(shù)類型、函數(shù)指針或者成
23、員指針。ret_cast 最普通的用途就是在函數(shù)指針類型之間進行轉(zhuǎn)換。doSomething()return 0;typedef void(*FuncPtr)(); /FuncPtr 函數(shù)指針類型ret_cast(&doSomething); /函數(shù)指針類型轉(zhuǎn)換FuncPtr funcPtr= re注意:reinpreter_cast 僅僅重新解釋類型,但沒有進行二進制的轉(zhuǎn)換。ret_cast(n); 在進行計算以后, d 包含無用值. 這n=9; double d=re是因為 re1.26 template 模板ret_cast 僅僅是n 的比特位到 d, 沒有進行必要的分析。templat
24、e與 template意義完全相同。 typename 關(guān)鍵字:template class X private:typename T:id i;在模板中,類型 T 限定了類型 id。 也就是說 id 取決于 T。這種情況下,編譯器不知道 id 是類型T 中的靜態(tài)成員變量,還是 T 的嵌套類。通過 typename 告訴編譯器將 T:id當(dāng)作類型,然后template 參數(shù):個變量 i,所以 typename 不可以出現(xiàn)在 T 的嵌套類前。template中的參數(shù)分為類型參數(shù)(T)和非類型參數(shù)(n)可以通過函數(shù)參數(shù)或者 class 的成員變量替換 template 非類型參數(shù)消除代碼膨脹tem
25、plate 隱式接口約束:template void doProsing(T &w)if (w.size() 10 & w != someNastyWidget) 對于 T(w 的類型)的隱式接口約束:(1) 它必須提供一個名為 size 的返回一個正數(shù)值的member function(成員函數(shù))。 operator!= 函數(shù)。(2) 它必須支持一個用于比較兩個類型template化:template化在編譯期執(zhí)行。如果又是 float,無法確定類型,報錯。template 特化:template class Compare/ speT 的對象的化時無法確定參數(shù)類型會報錯,例如 5 既是li
26、ze for floattemplateclass Compare/ spelize for doubletemplate class MsgSender /不含 send()方法template class LoggingMsgSender: public MsgSenderpany 特化為 CompanyZ 時,代碼不合法,特化的void sendClearMsg()send();MsgSender 不提供一般性的 template 接口令編譯器不會因為有可能進入特例化基類而使代碼失效的 3 個方法:(1) 使用 this-,this-send ();(2) 使用 using式,using
27、 MsgSender:send();(3) 明確被調(diào)用函數(shù)位于 base class 內(nèi),MsgSender:send()使用非類型參數(shù)在編譯期計算階乘:templatestruct Factorialenum value=n*Factorial:value; templatestruct Factorial enum value=1; coutFactorial:value派生類作用域-基類作用域-namespace 作用域-全局作用域內(nèi)層作用域中,如果內(nèi)層的作用域的函數(shù)或變量與外層作用域函數(shù)或變量同名,則會外層的函數(shù)或變量。例如派生類函數(shù)和基類函數(shù)同名會覆蓋基類函數(shù)(virtualfunc
28、tions 除外)。內(nèi)層作用域變量修改與外層作用域同名變量無關(guān)。main()i=0;1.30for (i=1; i= 5; +i)cout i;/輸出 1,2,3,4,5,覆蓋外層作用域的 icout i;/輸出 0,for 循環(huán)作用域中變量 i 的修改與 main 函數(shù)作用域中變量 i 無關(guān)typeid是個操作符,不是函數(shù)。typeid 操作符的返回結(jié)果是名為 type_info 的標(biāo)準(zhǔn)庫類型的對1.31象的。()返回類型的字符串,類型名字用系統(tǒng)相關(guān)的方法產(chǎn)生。cout typeid().name() endl;/輸出Derived d;Base &b2 =d;cout typeid(b2)
29、.name() endl/輸出 Base如果 Base 不包含虛函數(shù), typeid 的結(jié)果表達式的類型是 Base為 Base 加上一個虛函數(shù),couttypeid(b2).name()0 & y0)assert(xy);elseassert(ux);6.4 宏不是類型定義 #define T1 struct foo * typedef struct foo *T2;T1 a,b;/a 是 foo 型指針,b 是 foo 類型T2 a,b;/都是 foo 型指針大大1.511.52面1.531.54象(深入探索 C+對象模型)象 3 個特性:封裝性,繼承性,多態(tài)性面C 的結(jié)構(gòu)體變成 C+封裝
30、后的布局成本(主要的額外負(fù)擔(dān)由 virtual 引起):1、 virtual function 機制,用以支持一個有效率的“執(zhí)行期綁定”2、 virtual base class 用以實現(xiàn)“多次出現(xiàn)在繼承體系中的 base class,有一個單一而被共享的實體”C+對象模式C+有兩種 class data members:sic 和 nonsic三種 class member function:sic,nonsic 和 virtual簡單對象模型:一個 object 就是一系列的 slots,一個 slot 指向一個 member表格驅(qū)動對象模型:一個 object 含兩個指針,分別指向兩個表
31、:一個是 data member table,另一個 member function table這個含有多個 slot,每個 slot 指向一個成員函數(shù)1.55C+對象模型:Nons之外,sic 和 nons個步驟支持之:ic data members 放在每個 object 內(nèi),sic data members 在 object ic function members 也在 object 之外。Virtual functions 則以兩1、 每個 class 生成一個存放虛函數(shù)指針的 virtual table(vtbl)2、 每個 class object 被添加一個指針,指向相關(guān) vtb
32、l,這個指針被稱為 vptr。Vptr的第一個 slot 通常是每個class 所關(guān)聯(lián)的 type_inof object。vptr 的設(shè)定和重置都有每個 class 的構(gòu)造、析構(gòu)和拷貝構(gòu)造函數(shù)自動完成優(yōu)點: 空間和存取時間的效率缺點:如果程序代碼未改變,但是 objects 的 nons ic data members 有所修改,就需要重新編譯。Class object 的內(nèi)存需求1、nonsic data members 的總和大小 2、任何猶豫 alignment 的需求而填補上去的空1.56間 3、為了支持 virtual 而由產(chǎn)生的任何額外負(fù)擔(dān)指針的類型:指針類型會教導(dǎo)編譯器如何解釋
33、某個特定地址中的內(nèi)存內(nèi)容及其大小。這也就解釋了一個指向地址 1000 的類型為 void*的指針,不能通過它進行 object 操作 Class object 的內(nèi)存布局class ZooAnimal public: ZooAnimal();virtual ZooAnimal(); virtual void roe();protected:loc;/4 字節(jié)string name;/8 字節(jié);一個 ZooAnimal 指針講橫跨地址空間 10001015(4+8+4)加上多態(tài)class Bear:public ZooAnimal public: Bear();Bear();void roe()
34、;virtual void dance(); protected:enum Dan;1.571.58Dandan_know;cell_block;Bear b(“Yogi”); Bear *pb = &b;Bear b; ZooAnimal *pz = &b; Bear *pb = &b;pz 和pb 都指向Bear object 的第一個byte。差別是pb 所涵蓋的地址包含整個Bear object。而 pz 所涵蓋的地址只包含 ZooAnimal 部分。除了 ZooAnimal subobject 中出現(xiàn)的 members,不能使用 pz 來直接處理 Bear 的任何members,vi
35、rtual 機制除外。剩下一部分:深度探索 C+對象模型 P30自動默認(rèn)構(gòu)造函數(shù)1.59編譯器在需要的時候會為沒有任何構(gòu)造函數(shù)的類自動一個默認(rèn)構(gòu)造函數(shù),但是需要注意的是,該的構(gòu)造函數(shù)只會執(zhí)行編譯器所需要的行動,其他變量參數(shù)它不會幫你進行任何操作,比方說成員不會幫你初始化為 0 的!編譯器會自動默認(rèn)構(gòu)造函數(shù)的四種情況:1) A 自身沒有任何構(gòu)造函數(shù),但內(nèi)含一個有默認(rèn)構(gòu)造函數(shù)的 member object B的默認(rèn)構(gòu)造函數(shù)執(zhí)行的操作是調(diào)用 B 的 member object 的默認(rèn)構(gòu)造函數(shù)。 如果已經(jīng)有 A 的默認(rèn)構(gòu)造函數(shù),并初始化了 A 的某內(nèi)置類型對象。則編譯器會優(yōu)化 A 的默認(rèn)構(gòu)造函數(shù),調(diào)用
36、 B 的默認(rèn)構(gòu)造函數(shù)操作!如果有多個 class member objects 都需要 A 的默認(rèn)構(gòu)造函數(shù)進行初始化,則 C+會以這些對象在 class 中的次序來調(diào)用各類的默認(rèn)構(gòu)造函數(shù)2) A 自身沒有任何默認(rèn)構(gòu)造函數(shù)的類派生于一個帶有默認(rèn)構(gòu)造函數(shù)的基類它將調(diào)用上一層基類的默認(rèn)構(gòu)造函數(shù)。對一個后繼派生類來說,供的沒有區(qū)別。如果 A 沒有默認(rèn)構(gòu)造但有很多其他的構(gòu)造函數(shù),編譯器不會的和明確提新的默認(rèn)構(gòu)造函數(shù),而是直接擴展已有的每一個構(gòu)造函數(shù),加入調(diào)用基類的默認(rèn)構(gòu)造函數(shù)3) 帶有一個虛函數(shù)的 classClass(或繼承)一個 virtual functionClass 派生自一個繼承串鏈,其中有
37、一個或要有默認(rèn)構(gòu)造函數(shù)設(shè)置 vtbl 和 vptr4) 帶有一個虛基類的 class的 virtual base classes一種做法,派生類對象在每一個虛基類中安插一個指針,由指針指向虛基類。這個指針是在 class object 建構(gòu)期間被完成的Copy Constructor 的建構(gòu)操作(構(gòu)造函數(shù))1.60如果程序員沒有copy constructor,編譯器將自動生成生成 default copy constructor,default copy constructor 使用 bitwise copy(按位拷貝會把 vptr 也一模一樣都拷貝過去,但是如果派生類用 bitwise c
38、opy 拷貝給基類的話,基類的 vptr 指向的就不是自己的虛函數(shù)表了,指向的是派生類的虛函數(shù)表,所以,這種情況下不能使用按位拷貝)編譯器不使用 bitwise copy 的 default copy constructor 四種情況:calss 內(nèi)含 member object,member object 有copy constructorclass 繼承自 base class,base class 有 copy constructor(3) classvirtual functions(4) class 繼承鏈中有 virtual base class前兩種情況中編譯器必須將 membe
39、r 或 base class 的構(gòu)造調(diào)用操作按吵到被的構(gòu)造函數(shù)中。第三點參看上面紅色話。第四點中,一個 class object 如果以另一個 object 作為初值,而后者有一個虛基類subobject,那么會使按位拷貝 sems 失效。P60bitwise copy,又名 shallow copy,是淺。memberwise copy,又名 deep copy,是深。注意:(1)A(A other)參拷貝到實參會調(diào)用構(gòu)造函數(shù)中傳入的參數(shù)是 A 的一個實例。由于是傳值,把形構(gòu)造函數(shù)。因此如果允許構(gòu)造函數(shù)傳值,那么會形成永無休止的遞歸并造成棧溢出。因此 C+的標(biāo)準(zhǔn)不允許構(gòu)造函數(shù)傳值參數(shù),而必須
40、是傳或者常量,例如 A(A &other)和 A(const A &other)。(2)函數(shù)中傳入的參數(shù)是 class 的實例時,調(diào)用 copy constructor;函數(shù)中傳入的參數(shù)是class 的指針或者時,不調(diào)用 copy constructor;函數(shù)的返回值為 class 的實例時,編譯器會將 X fun()X result=; return result)轉(zhuǎn)化為 fun(X &result)result=; return;,避免返回值時調(diào)用 copy constructor,稱為 NRV(Named Return Value)優(yōu)化。= fun();/就是這個意思了!(3)派生類對象
41、時,對于派生類繼承的基類成員變量也要。對于基類的 privatedata member,派生類可以通過調(diào)用基類的函數(shù)賦值函數(shù)防止自我賦值并且返回 reference to *thisWidget& Widget:operator=(const Widget &rhs)if(this=rhs)return *this;return *this;C+自動生成的函數(shù)。1.611.62如果程序員沒有constructor, destructor, copy constructor, copy assignment operator,編譯器將自動生成這些函數(shù)。如果要可以將函數(shù)C+自動生成的函數(shù)(cons
42、tructor,copy constructor,copy assignment operator),為 private 而且故意不實現(xiàn)。構(gòu)造函數(shù)和析構(gòu)函數(shù)(1) 如果程序員沒有1.63任何 constructor,編譯器將自動生成 default constructor,default constructor 沒有參數(shù),只執(zhí)行編譯器所需的行動,不會對成員變量進行初始化(例如將整形變量初始化為 0)(2) polymorphic base class 應(yīng)該virtual destructor,避免部分 destructor。構(gòu)造函數(shù)的名字必須與類名相同,可以有任意類型的參數(shù),但不能具有返回類型
43、;析構(gòu)函數(shù)的名字必須與類名相同,但它前面必須加一個波浪號,析構(gòu)函數(shù)沒有參數(shù),也沒有返回值,而且不能被重載。使用無參數(shù)構(gòu)造函數(shù)定義對象,不需要加括號。class Test Test() Test t();定義出錯,編譯正確,但不會調(diào)用構(gòu)造函數(shù)。Test t;定義正確,調(diào)用構(gòu)造函數(shù)。構(gòu)造函數(shù)調(diào)用順序:(1) virtual base class 調(diào)用構(gòu)造函數(shù)(virtual base class 的構(gòu)造函數(shù)由最底層 derived class 調(diào)用,其他 derived class 不再調(diào)用其構(gòu)造函數(shù),通過 most_derived 標(biāo)識符確定是否調(diào)用 virtual base class 的構(gòu)造
44、函數(shù))(2) base class 按順序調(diào)用構(gòu)造函數(shù)(3) vptr 重置,指向當(dāng)前 class 的 virtual tablemember object 按調(diào)用自身構(gòu)造函數(shù)析構(gòu)函數(shù)調(diào)用順序:調(diào)用自身析構(gòu)函數(shù)member object 按順序調(diào)用構(gòu)造函數(shù)順序的相反順序調(diào)用析構(gòu)函數(shù)(3) vptr 重置,指向適當(dāng)?shù)?base class 的 virtual table(4) base class 按順序的相反順序調(diào)用析構(gòu)函數(shù)(5) virtual base class 按原構(gòu)造順序的相反順序調(diào)用析構(gòu)函數(shù)sic object 的構(gòu)造和析構(gòu)global sic object:_sti()函數(shù)進靜態(tài)
45、初始化操作和_std()函數(shù)進行靜態(tài)內(nèi)存 main()函數(shù)前調(diào)用,_std()函數(shù)在 main()結(jié)束前調(diào)用。操。local sic object:第一次進入函數(shù),臨時對象為 false,constructor 被調(diào)用,然后臨時對象改為 true。destructor 在與”text program file”(例如 sic.c)有關(guān)的靜態(tài)內(nèi)存函數(shù)中被調(diào)用。為了 local sic objects 能夠按照構(gòu)造的相反順序調(diào)用 destructor,需要對產(chǎn)生出來的 local sic objects 保持一個執(zhí)行期鏈表。 array object: 使用 vec_new()的函數(shù),生成 cla
46、ss object 數(shù)組。 vec_new(void *array,/*數(shù)組起始地址*/size_em_size,/*每個 class object 大小*/ elem_count,/*數(shù)組元素數(shù)目*/void (*constructor)(void*),/*constructor 函數(shù)指針*/ void(*destructor)(void*)/*destructor 是函數(shù)指針*/對于明顯獲得初值的元素,vec_new 不再有必要。p10=Po(),Po(1,1,5),Po(),明確初始化前 3 個元素,vec_new 只初始化后Po7 個元素。初始化C+中類的成員不會默認(rèn)初始化(整形初始化
47、為 0)1.64C+使用初始化列表 Po:Po(初始化的順序取決于變量的x,y):x(x),y(y)進行初始化次序,與初始化列表的次序無關(guān)C+規(guī)定對象變量的初始化動作發(fā)生在進入構(gòu)造函數(shù)體之前決不再構(gòu)造和析構(gòu)過程中調(diào)用 virtual 函數(shù)盡量不要在初始化時使用virtual function,因為vptr 的設(shè)置和初始化的順序并不確定,可能無法調(diào)用正確的 virtual function。如果需要調(diào)用 virtual function,可以在構(gòu)造函數(shù)體內(nèi)調(diào)用。Po :Po ( x, y)this-x=x;this-y=y;是賦值,不是初始化函數(shù)內(nèi)的s ic object 稱為local s i
48、c object,其他s ic object 稱為non-local satic object C+對定義于不同編譯單元的 non-local s ic object 的初始化次序無明確定義。如果某編譯單元的 non-local s ic ojbect 的初始化動作使用了另一個編譯單元的 non-local s ic object,所用的 oject 可能尚未初始化。解決方法是將 non-s ic local s ic object移動到自己專屬的函數(shù)內(nèi)。這些函數(shù)返回一個 reference 指向它所含對象,然后用戶調(diào)用這些函數(shù),而不直接涉及這些對象。EffectiveC+條款 04字節(jié)對齊1
49、.65計算機系統(tǒng)中以 byte 為數(shù)據(jù),不同數(shù)據(jù)類型所占的空間不同,如:占 4 個字節(jié),char 占一個字節(jié),short 占兩個字節(jié)計算機為了快速的讀寫數(shù)據(jù),默認(rèn)情況下將數(shù)據(jù)存放在某個地址的起始位置,如:類型默認(rèn)在地址能被 4整除的起始位置,char 可以存放在任何地址位置(被 1 整除),short 類型能被 2 整除的其實位置。這是默認(rèn)字節(jié)對齊。在地址#pragma pack(n)來設(shè)定變量以 n 字節(jié)對齊方式。n 字節(jié)對齊就是說變量存放的起始地址的偏移量有兩種情況:第一、如果 n 大于等于該變量所占用的字節(jié)數(shù),那么偏移量必須滿足默認(rèn)的對齊方式,第二、如果 n 小于該變量的類型所占用的字節(jié)
50、數(shù),那么偏移量為 n 的倍數(shù),不用滿足默認(rèn)的對齊方式。類的大小1.66空類的大小為 1 字節(jié),會入一個 char 以確保空類實例化有獨一無二的地址。一個空基類 X,兩個派生類 Y,Z承自 X,派生類 A 繼承自Y 和 Z類大小主要受三點影響:1、語言本身造成的負(fù)擔(dān) 2、編譯器的特別處理 3、對齊限制則在未特別處理empty virtual base class 的編譯器情況下,X 的 sizeof 大小是 1 個字節(jié), Y 和 Z 是 8,A 的大小是 12有特別處理的編譯器中,X 為 1,Y 和 Z 是 4,A 是 8 個字節(jié)大小class A;/sizeof 大小為 1class B:vi
51、rtual public A;class C:virtual public A;/sizeof 大小為 4 class D:public B,public C;/sizeof 大小為 8class E:public B; class F:public C;/sizeof 大小為 4 class G:public E, public F;/sizeof 大小為 8class H:virtual public B;class I:virtual public C;/sizeof 大小為 8 class J:public H, public I;/sizeof 大小為 16成員函數(shù),靜態(tài)成員變量與類
52、的大小無關(guān)的。虛函數(shù)需要指針指向虛函數(shù)表,虛基類需要指針指向虛基類,每個指針大小占4 字節(jié)。類的大小遵守字節(jié)對齊,調(diào)整規(guī)則。成員變量成員變量布局:1.67同一 acs section 中 data member 不一定是連續(xù)的,只要符合后在高地址即可(邊界調(diào)整就需要填補一些 bytes),多個 acs section 中的 data member排列。凡處于同一 acs sections 中的數(shù)據(jù),排列必定保證以其次序出現(xiàn)在內(nèi)存布局當(dāng)中。vptr 放在 class object 最前端或者最后,取決于編譯器(或者任何哪里)。C+標(biāo)準(zhǔn)允許編譯器將多個 acs sections 的 data me
53、mbers排列,但是現(xiàn)在一般都是各 sections 連鎖在一起,依照的次序,成為一塊連續(xù)區(qū)塊base class data member 先出現(xiàn),virtual base class data member 在最后。成員變量存?。簊ic data member 存?。簊ic data member 只有一個實體,存放在數(shù)據(jù)段中,每次訪問內(nèi)化為對唯一實體的直接origin.sum 內(nèi)化為 Po:sum。sic member 的地址是指向 sic member 數(shù)據(jù)類型的指針,&Po:sum 的類型是*。如果兩個 class 的sic member 同名(它們都被放在程序的 data segme
54、nt 里),編譯器會對 sic member編碼,稱為 name-mangling。non-sic data member 存取:non-sic member 存放在每個 class object 中。Po3d *pt3d; pt3d-x = 0.0; 其執(zhí)行效率在x 是一個struct member、一個 class member、單一繼承、多重繼承的情況下都完全相同。但 x 是一個 virtual base class 的 member,存取速度會比較慢一點。即對于多態(tài),data member 的 offset 必須到執(zhí)行期的時候才能確定.問題:Po3d origin, *pt=&orig
55、in;origin.x=0.0; pt-x=0.0;通過 origin 和 pt 存取大差異嗎?(p99)重單一繼承中,data member 是直接存放在 class object 中,引入繼承不影響存取效率。具體繼承(相對于虛擬繼承)并不會增加空間活存取時間上的額外負(fù)擔(dān)成員函數(shù)三種類型的成員函數(shù):1.68(1)sic member functions只能存取sic data member,不能存取 non-sic data membe。不能為const,volatile 或 virtual。(連 this 指針都沒有,當(dāng)然不能non-sic member functions為 const
56、了)(2)non-s ic member functions 會內(nèi)化為 non-member 的形式,p-Po :fun()內(nèi)化為 Po :fun(p),其中 p 就是 this 指針,即使 p 是空指針仍然可以調(diào)用。如果 member functions 存取 non-s ic data member,空指針調(diào)用 member function 出錯。virtual functionsclass object 內(nèi)含 N 個 vptr 指向 virtual table(N 為 base class 數(shù)目)。virtual function調(diào)用會內(nèi)化為(*p-vptri)(p),其中 vptri
57、為指向 virtual function 的函數(shù)指針智能指針(同名 virtual function 在virtual table 中的索引值是相同的)。(3)多繼承單繼承中,base class 的指針和 derived class 指針都指向相同的起始地址,不需要修改地址;多繼承中,第二個或后繼的 base class 指針的地址需要修改地址。例如,Vertex3d 繼承自 Po3d 和 Vertex,Po3d 繼承自 Po2d。 Po2d *p2d; Po3d *p3d; Vertex *pv; Vertex3d v3d;p2d=&v3d 和 p3d=&v3d 不需要修改地址;pv=&v
58、3d 需要修改地址。多重繼承中,為解決歧義,必須明確指明調(diào)用哪個 base class 內(nèi)的變量或函數(shù)。用 C+設(shè)計一個不能被繼承的類。(劍指 offer 面試題 48)template class MakeFinalfriend T; private:MakeFinal() MakeFinal() ;class FinalClass : virtual public MakeFinalpublic: FinalClass() FinalClass() ;FinalClass 使用與一般類沒有區(qū)別,盡管類 MakeFinal 的構(gòu)1.691.70造函數(shù)和析構(gòu)函數(shù)都是私有的,但由于類 Final
59、Class 是它的函數(shù),因此在FinalClass 中調(diào)用 MakeFinal 的構(gòu)造函數(shù)和析構(gòu)函數(shù)都不會造成編譯錯誤。但從 FinalClass 繼承一個類并創(chuàng)建它的實例時,卻不能通過編譯,例如 class Try : public FinalClass,由于類 FinalClass 是從類 MakeFinal 承過來的,在調(diào)用 Try 的構(gòu)造函數(shù)的時候,會直接跳過FinalClass 而直接調(diào)用MakeFinal 的構(gòu)造函數(shù)。Try 不是 MakeFinal 的能調(diào)用其私有的構(gòu)造函數(shù)。承,因此不1.71虛擬繼承中,virtual base class 只有一個實體。每個有 virtual
60、base class 的 class 必須有一個指針 vbptr 指向每個 virtual base class。A 虛擬繼承自 B,B 虛擬繼承自 C,則 A 有兩個指針指向兩個 virtual base class虛函數(shù)(1) virtual 函數(shù)表現(xiàn)在運行期,是運行期多態(tài)(動態(tài)多態(tài)),函數(shù)重載和 template 參數(shù)具現(xiàn)化表現(xiàn)在編譯期,是編譯期多態(tài)(靜態(tài)多態(tài))。1.72(2) 類的實例不支持 virtual 函數(shù)多態(tài),類的指針和例如 Bear 繼承自 ZooAnimal,roe 是 virtual 函數(shù)支持 virtual 函數(shù)多態(tài)。zr.roe()/ 調(diào)用 Bear b; ZooAn
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 保護鼻子小班健康教案反思
- 課阿拉伯帝國說課稿
- 環(huán)境污染治理分層管理辦法
- 二手房交易市場動態(tài)
- 養(yǎng)殖場客戶服務(wù)與滿意度
- 藥店設(shè)備養(yǎng)護管理辦法
- 公路聲屏障維護合同范本
- 文化產(chǎn)業(yè)招投標(biāo)技術(shù)標(biāo)范本
- 談判技巧培訓(xùn)委托協(xié)議
- 城市供水合同談判教案
- 法檢商品目錄
- 中國恒大集團籌資狀況分析
- 消防火災(zāi)自動報警主機更換(增加)施工方案
- 《加盟申請表》word版
- 鋼絲繩的規(guī)格和意義
- profibus現(xiàn)場總線故障診斷與排除
- 高考數(shù)學(xué)立體幾何中的翻折、軌跡及最值(范圍)問題
- 大學(xué)生生涯決策平衡單樣表
- 膠凝砂礫石施工方案
- 小學(xué)德育課程校本教材
- 金光修持法(含咒訣指印、步驟、利益說明)
評論
0/150
提交評論