70道C語(yǔ)言與C++常見(jiàn)問(wèn)答題
1 變量的聲明和定義有什么區(qū)別
變量的定義為變量分配地址和存儲(chǔ)空間, 變量的聲明不分配地址。一個(gè)變量可以在多個(gè)地方聲明, 但是只在一個(gè)地方定義。加入extern 修飾的是變量的聲明,說(shuō)明此變量將在文件以外或在文件后面部分定義。
說(shuō)明:很多時(shí)候一個(gè)變量,只是聲明不分配內(nèi)存空間,直到具體使用時(shí)才初始化,分配內(nèi)存空間, 如外部變量。
int main()
{
extern int A;
//這是個(gè)聲明而不是定義,聲明A是一個(gè)已經(jīng)定義了的外部變量
//注意:聲明外部變量時(shí)可以把變量類(lèi)型去掉如:extern A;
dosth(); //執(zhí)行函數(shù)
}
int A; //是定義,定義了A為整型的外部變量
2 簡(jiǎn)述#ifdef、#else、#endif和#ifndef的作用
利用#ifdef、#endif將某程序功能模塊包括進(jìn)去,以向特定用戶提供該功能。在不需要時(shí)用戶可輕易將其屏蔽。
#ifdef MATH #include "math.c" #endif
在子程序前加上標(biāo)記,以便于追蹤和調(diào)試。
#ifdef DEBUG printf ("Indebugging......!"); #endif
應(yīng)對(duì)硬件的限制。由于一些具體應(yīng)用環(huán)境的硬件不一樣,限于條件,本地缺乏這種設(shè)備,只能繞過(guò)硬件,直接寫(xiě)出預(yù)期結(jié)果。
「注意」:雖然不用條件編譯命令而直接用if語(yǔ)句也能達(dá)到要求,但那樣做目標(biāo)程序長(zhǎng)(因?yàn)樗姓Z(yǔ)句都編譯),運(yùn)行時(shí)間長(zhǎng)(因?yàn)樵诔绦蜻\(yùn)行時(shí)間對(duì)if語(yǔ)句進(jìn)行測(cè)試)。而采用條件編譯,可以減少被編譯的語(yǔ)句,從而減少目標(biāo)程序的長(zhǎng)度,減少運(yùn)行時(shí)間。
3 寫(xiě)出int 、bool、 float 、指針變量與 “零值”比較的if 語(yǔ)句
//int與零值比較 if ( n == 0 ) if ( n != 0 ) //bool與零值比較 if (flag) // 表示flag為真 if (!flag) // 表示flag為假 //float與零值比較 const float EPSINON = 0.00001; if ((x >= - EPSINON) && (x <= EPSINON) //其中EPSINON是允許的誤差(即精度)。 //指針變量與零值比較 if (p == NULL) if (p != NULL)
4 結(jié)構(gòu)體可以直接賦值嗎
聲明時(shí)可以直接初始化,同一結(jié)構(gòu)體的不同對(duì)象之間也可以直接賦值,但是當(dāng)結(jié)構(gòu)體中含有指針“成員”時(shí)一定要小心。
「注意」:當(dāng)有多個(gè)指針指向同一段內(nèi)存時(shí),某個(gè)指針釋放這段內(nèi)存可能會(huì)導(dǎo)致其他指針的非法操作。因此在釋放前一定要確保其他指針不再使用這段內(nèi)存空間。
5 sizeof 和strlen 的區(qū)別
- sizeof是一個(gè)操作符,strlen是庫(kù)函數(shù)。
- sizeof的參數(shù)可以是數(shù)據(jù)的類(lèi)型,也可以是變量,而strlen只能以結(jié)尾為‘\0’的字符串作參數(shù)。
- 編譯器在編譯時(shí)就計(jì)算出了sizeof的結(jié)果,而strlen函數(shù)必須在運(yùn)行時(shí)才能計(jì)算出來(lái)。并且sizeof計(jì)算的是數(shù)據(jù)類(lèi)型占內(nèi)存的大小,而strlen計(jì)算的是字符串實(shí)際的長(zhǎng)度。
- 數(shù)組做sizeof的參數(shù)不退化,傳遞給strlen就退化為指針了
6 C 語(yǔ)言的關(guān)鍵字 static 和 C++ 的關(guān)鍵字 static 有什么區(qū)別
在 C 中 static 用來(lái)修飾局部靜態(tài)變量和外部靜態(tài)變量、函數(shù)。而 C++中除了上述功能外,還用來(lái)定義類(lèi)的成員變量和函數(shù)。即靜態(tài)成員和靜態(tài)成員函數(shù)。
「注意」:編程時(shí) static 的記憶性,和全局性的特點(diǎn)可以讓在不同時(shí)期調(diào)用的函數(shù)進(jìn)行通信,傳遞信息,而 C++的靜態(tài)成員則可以在多個(gè)對(duì)象實(shí)例間進(jìn)行通信,傳遞信息。
7 C 語(yǔ)言的 malloc 和 C++ 中的 new 有什么區(qū)別
- new 、delete 是操作符,可以重載,只能在C++ 中使用。
- malloc、free 是函數(shù),可以覆蓋,C、C++ 中都可以使用。
- new 可以調(diào)用對(duì)象的構(gòu)造函數(shù),對(duì)應(yīng)的delete 調(diào)用相應(yīng)的析構(gòu)函數(shù)。
- malloc 僅僅分配內(nèi)存,free 僅僅回收內(nèi)存,并不執(zhí)行構(gòu)造和析構(gòu)函數(shù)
- new 、delete 返回的是某種數(shù)據(jù)類(lèi)型指針,malloc、free 返回的是void 指針。
「注意」:malloc 申請(qǐng)的內(nèi)存空間要用free 釋放,而new 申請(qǐng)的內(nèi)存空間要用delete 釋放,不要混用。
8 寫(xiě)一個(gè) “標(biāo)準(zhǔn)”宏MIN
#define min(a,b)((a)<=(b)?(a):(b))
9 ++i和i++的區(qū)別
++i先自增1,再返回,i++先返回i,再自增1
10 volatile有什么作用
- 狀態(tài)寄存器一類(lèi)的并行設(shè)備硬件寄存器。
- 一個(gè)中斷服務(wù)子程序會(huì)訪問(wèn)到的非自動(dòng)變量。
- 多線程間被幾個(gè)任務(wù)共享的變量。
「注意」:雖然volatile在嵌入式方面應(yīng)用比較多,但是在PC軟件的多線程中,volatile修飾的臨界變量也是非常實(shí)用的。
11 一個(gè)參數(shù)可以既是const又是volatile嗎
可以,用const和volatile同時(shí)修飾變量,表示這個(gè)變量在程序內(nèi)部是只讀的,不能改變的,只在程序外部條件變化下改變,并且編譯器不會(huì)優(yōu)化這個(gè)變量。每次使用這個(gè)變量時(shí),都要小心地去內(nèi)存讀取這個(gè)變量的值,而不是去寄存器讀取它的備份。
注意:在此一定要注意const的意思,const只是不允許程序中的代碼改變某一變量,其在編譯期發(fā)揮作用,它并沒(méi)有實(shí)際地禁止某段內(nèi)存的讀寫(xiě)特性。
12 a 和&a 有什么區(qū)別
&a:其含義就是“變量a的地址”。
*a:用在不同的地方,含義也不一樣。
- 在聲明語(yǔ)句中,*a只說(shuō)明a是一個(gè)指針變量,如int *a;
- 在其他語(yǔ)句中,*a前面沒(méi)有操作數(shù)且a是一個(gè)指針時(shí),*a代表指針a指向的地址內(nèi)存放的數(shù)據(jù),如b=*a;
- *a前面有操作數(shù)且a是一個(gè)普通變量時(shí),a代表乘以a,如c=ba。
13 用C 編寫(xiě)一個(gè)死循環(huán)程序
while(1)
{ }
「注意」:很多種途徑都可實(shí)現(xiàn)同一種功能,但是不同的方法時(shí)間和空間占用度不同,特別是對(duì)于嵌入 式軟件,處理器速度比較慢,存儲(chǔ)空間較小,所以時(shí)間和空間優(yōu)勢(shì)是選擇各種方法的首要考慮條件。
14 結(jié)構(gòu)體內(nèi)存對(duì)齊問(wèn)題
請(qǐng)寫(xiě)出以下代碼的輸出結(jié)果:
#includestruct S1 { int i:8; char j:4; int a:4; double b; }; struct S2 { int i:8; char j:4; double b; int a:4; }; struct S3 { int i; char j; double b; int a; }; int main() { printf("%d\n",sizeof(S1)); // 輸出8 printf("%d\n",sizeof(S1); // 輸出12 printf("%d\n",sizeof(Test3)); // 輸出8 return 0; } sizeof(S1)=16 sizeof(S2)=24 sizeof(S3)=32
「說(shuō)明」:結(jié)構(gòu)體作為一種復(fù)合數(shù)據(jù)類(lèi)型,其構(gòu)成元素既可以是基本數(shù)據(jù)類(lèi)型的變量,也可以是一些復(fù)合型類(lèi)型數(shù)據(jù)。對(duì)此,編譯器會(huì)自動(dòng)進(jìn)行成員變量的對(duì)齊以提高運(yùn)算效率。默認(rèn)情況下,按自然對(duì)齊條件分配空間。各個(gè)成員按照它們被聲明的順序在內(nèi)存中順序存儲(chǔ),第一個(gè)成員的地址和整個(gè)結(jié)構(gòu)的地址相同,向結(jié)構(gòu)體成員中size最大的成員對(duì)齊。
許多實(shí)際的計(jì)算機(jī)系統(tǒng)對(duì)基本類(lèi)型數(shù)據(jù)在內(nèi)存中存放的位置有限制,它們會(huì)要求這些數(shù)據(jù)的首地址的值是某個(gè)數(shù)k(通常它為4或8)的倍數(shù),而這個(gè)k則被稱(chēng)為該數(shù)據(jù)類(lèi)型的對(duì)齊模數(shù)。
15 全局變量和局部變量有什么區(qū)別?實(shí)怎么實(shí)現(xiàn)的?操作系統(tǒng)和編譯器是怎么知道的?
- 全局變量是整個(gè)程序都可訪問(wèn)的變量,誰(shuí)都可以訪問(wèn),生存期在整個(gè)程序從運(yùn)行到結(jié)束(在程序結(jié)束時(shí)所占內(nèi)存釋放);
- 而局部變量存在于模塊(子程序,函數(shù))中,只有所在模塊可以訪問(wèn),其他模塊不可直接訪問(wèn),模塊結(jié)束(函數(shù)調(diào)用完畢),局部變量消失,所占據(jù)的內(nèi)存釋放。
- 操作系統(tǒng)和編譯器,可能是通過(guò)內(nèi)存分配的位置來(lái)知道的,全局變量分配在全局?jǐn)?shù)據(jù)段并且在程序開(kāi)始運(yùn)行的時(shí)候被加載.局部變量則分配在堆棧里面。
16 簡(jiǎn)述C、C++程序編譯的內(nèi)存分配情況
- 從靜態(tài)存儲(chǔ)區(qū)域分配:
內(nèi)存在程序編譯時(shí)就已經(jīng)分配好,這塊內(nèi)存在程序的整個(gè)運(yùn)行期間都存在。速度快、不容易出錯(cuò), 因?yàn)橛邢到y(tǒng)會(huì)善后。例如全局變量,static 變量,常量字符串等。
- 在棧上分配:
在執(zhí)行函數(shù)時(shí),函數(shù)內(nèi)局部變量的存儲(chǔ)單元都在棧上創(chuàng)建,函數(shù)執(zhí)行結(jié)束時(shí)這些存儲(chǔ)單元自動(dòng)被釋 放。棧內(nèi)存分配運(yùn)算內(nèi)置于處理器的指令集中,效率很高,但是分配的內(nèi)存容量有限。大小為2M。
- 從堆上分配:
即動(dòng)態(tài)內(nèi)存分配。程序在運(yùn)行的時(shí)候用 malloc 或new 申請(qǐng)任意大小的內(nèi)存,程序員自己負(fù)責(zé)在何 時(shí)用free 或delete 釋放內(nèi)存。動(dòng)態(tài)內(nèi)存的生存期由程序員決定,使用非常靈活。如果在堆上分配了空間,就有責(zé)任回收它,否則運(yùn)行的程序會(huì)出現(xiàn)內(nèi)存泄漏,另外頻繁地分配和釋放不同大小的堆空間將會(huì)產(chǎn)生 堆內(nèi)碎塊。
一個(gè)C、C++程序編譯時(shí)內(nèi)存分為5 大存儲(chǔ)區(qū):堆區(qū)、棧區(qū)、全局區(qū)、文字常量區(qū)、程序代碼區(qū)。
17 簡(jiǎn)述strcpy、sprintf 與memcpy 的區(qū)別
- 操作對(duì)象不同,strcpy 的兩個(gè)操作對(duì)象均為字符串,sprintf 的操作源對(duì)象可以是多種數(shù)據(jù)類(lèi)型, 目的操作對(duì)象是字符串,memcpy 的兩個(gè)對(duì)象就是兩個(gè)任意可操作的內(nèi)存地址,并不限于何種數(shù)據(jù)類(lèi)型。
- 執(zhí)行效率不同,memcpy 最高,strcpy 次之,sprintf 的效率最低。
- 實(shí)現(xiàn)功能不同,strcpy 主要實(shí)現(xiàn)字符串變量間的拷貝,sprintf 主要實(shí)現(xiàn)其他數(shù)據(jù)類(lèi)型格式到字 符串的轉(zhuǎn)化,memcpy 主要是內(nèi)存塊間的拷貝。
「注意」:strcpy、sprintf 與memcpy 都可以實(shí)現(xiàn)拷貝的功能,但是針對(duì)的對(duì)象不同,根據(jù)實(shí)際需求,來(lái) 選擇合適的函數(shù)實(shí)現(xiàn)拷貝功能。
18 請(qǐng)解析((void ()( ) )0)( )的含義
- void (*0)( ) :是一個(gè)返回值為void,參數(shù)為空的函數(shù)指針0。
- (void (*)( ))0:把0轉(zhuǎn)變成一個(gè)返回值為void,參數(shù)為空的函數(shù)指針。
- (void ()( ))0:在上句的基礎(chǔ)上加*表示整個(gè)是一個(gè)返回值為void,無(wú)參數(shù),并且起始地址為0的函數(shù)的名字。
- ( (void ()( ))0)( ):這就是上句的函數(shù)名所對(duì)應(yīng)的函數(shù)的調(diào)用。
19 C語(yǔ)言的指針和引用和c++的有什么區(qū)別?
- 指針有自己的一塊空間,而引用只是一個(gè)別名;
- 使用sizeof看一個(gè)指針的大小是4,而引用則是被引用對(duì)象的大??;
- 作為參數(shù)傳遞時(shí),指針需要被解引用才可以對(duì)對(duì)象進(jìn)行操作,而直接對(duì)引 用的修改都會(huì)改變引用所指向的對(duì)象;
- 可以有const指針,但是沒(méi)有const引用;
- 指針在使用中可以指向其它對(duì)象,但是引用只能是一個(gè)對(duì)象的引用,不能 被改變;
- 指針可以有多級(jí)指針(**p),而引用止于一級(jí);
- 指針和引用使用++運(yùn)算符的意義不一樣;
- 如果返回動(dòng)態(tài)內(nèi)存分配的對(duì)象或者內(nèi)存,必須使用指針,引用可能引起內(nèi)存泄露。
20 typedef 和define 有什么區(qū)別
- 用法不同:typedef 用來(lái)定義一種數(shù)據(jù)類(lèi)型的別名,增強(qiáng)程序的可讀性。define 主要用來(lái)定義 常量,以及書(shū)寫(xiě)復(fù)雜使用頻繁的宏。
- 執(zhí)行時(shí)間不同:typedef 是編譯過(guò)程的一部分,有類(lèi)型檢查的功能。define 是宏定義,是預(yù)編譯的部分,其發(fā)生在編譯之前,只是簡(jiǎn)單的進(jìn)行字符串的替換,不進(jìn)行類(lèi)型的檢查。
- 作用域不同:typedef 有作用域限定。define 不受作用域約束,只要是在define 聲明后的引用 都是正確的。
- 對(duì)指針的操作不同:typedef 和define 定義的指針時(shí)有很大的區(qū)別。
「注意」:typedef 定義是語(yǔ)句,因?yàn)榫湮惨由戏痔?hào)。而define 不是語(yǔ)句,千萬(wàn)不能在句尾加分號(hào)。
21 指針常量與常量指針區(qū)別
指針常量是指定義了一個(gè)指針,這個(gè)指針的值只能在定義時(shí)初始化,其他地方不能改變。常量指針 是指定義了一個(gè)指針,這個(gè)指針指向一個(gè)只讀的對(duì)象,不能通過(guò)常量指針來(lái)改變這個(gè)對(duì)象的值。指針常量強(qiáng)調(diào)的是指針的不可改變性,而常量指針強(qiáng)調(diào)的是指針對(duì)其所指對(duì)象的不可改變性。
「注意」:無(wú)論是指針常量還是常量指針,其最大的用途就是作為函數(shù)的形式參數(shù),保證實(shí)參在被調(diào)用 函數(shù)中的不可改變特性。
22 簡(jiǎn)述隊(duì)列和棧的異同
隊(duì)列和棧都是線性存儲(chǔ)結(jié)構(gòu),但是兩者的插入和刪除數(shù)據(jù)的操作不同,隊(duì)列是“先進(jìn)先出”,棧是 “后進(jìn)先出”。
「注意」:區(qū)別棧區(qū)和堆區(qū)。堆區(qū)的存取是“順序隨意”,而棧區(qū)是“后進(jìn)先出”。棧由編譯器自動(dòng)分 配釋放 ,存放函數(shù)的參數(shù)值,局部變量的值等。其操作方式類(lèi)似于數(shù)據(jù)結(jié)構(gòu)中的棧。堆一般由程序員 分配釋放, 若程序員不釋放,程序結(jié)束時(shí)可能由OS 回收。分配方式類(lèi)似于鏈表。它與本題中的堆和棧是兩回事。堆棧只是一種數(shù)據(jù)結(jié)構(gòu),而堆區(qū)和棧區(qū)是程序的不同內(nèi)存存儲(chǔ)區(qū)域。
23 設(shè)置地址為0x67a9 的整型變量的值為0xaa66
int *ptr; ptr = (int *)0x67a9; *ptr = 0xaa66;
「注意」:這道題就是強(qiáng)制類(lèi)型轉(zhuǎn)換的典型例子,無(wú)論在什么平臺(tái)地址長(zhǎng)度和整型數(shù)據(jù)的長(zhǎng)度是一樣的, 即一個(gè)整型數(shù)據(jù)可以強(qiáng)制轉(zhuǎn)換成地址指針類(lèi)型,只要有意義即可。
24 編碼實(shí)現(xiàn)字符串轉(zhuǎn)化為數(shù)字
編碼實(shí)現(xiàn)函數(shù)atoi(),設(shè)計(jì)一個(gè)程序,把一個(gè)字符串轉(zhuǎn)化為一個(gè)整型數(shù)值。例如數(shù)字:“5486321 ”, 轉(zhuǎn)化成字符:5486321。
int myAtoi(const char * str) { int num = 0; //保存轉(zhuǎn)換后的數(shù)值 int isNegative = 0; //記錄字符串中是否有負(fù)號(hào) int n =0; char *p = str; if(p == NULL) //判斷指針的合法性 { return -1; } while(*p++ != '\0') //計(jì)算數(shù)字符串度 { n++; } p = str; if(p[0] == '-') //判斷數(shù)組是否有負(fù)號(hào) { isNegative = 1; } char temp = '0'; for(int i = 0 ; i < n; i++) { char temp = *p++; if(temp > '9' ||temp < '0') //濾除非數(shù)字字符 { continue; } if(num !=0 || temp != '0') //濾除字符串開(kāi)始的0 字符 { temp -= 0x30; //將數(shù)字字符轉(zhuǎn)換為數(shù)值 num += temp *int( pow(10 , n - 1 -i) ); } } if(isNegative) //如果字符串中有負(fù)號(hào),將數(shù)值取反 { return (0 - num); } else { return num; //返回轉(zhuǎn)換后的數(shù)值 } }
25 C語(yǔ)言的結(jié)構(gòu)體和C++的有什么區(qū)別
- C語(yǔ)言的結(jié)構(gòu)體是不能有函數(shù)成員的,而C++的類(lèi)可以有。
- C語(yǔ)言的結(jié)構(gòu)體中數(shù)據(jù)成員是沒(méi)有private、public和protected訪問(wèn)限定的。而C++的類(lèi)的成員有這些訪問(wèn)限定。
- C語(yǔ)言的結(jié)構(gòu)體是沒(méi)有繼承關(guān)系的,而C++的類(lèi)卻有豐富的繼承關(guān)系。
「注意」:雖然C的結(jié)構(gòu)體和C++的類(lèi)有很大的相似度,但是類(lèi)是實(shí)現(xiàn)面向?qū)ο蟮幕A(chǔ)。而結(jié)構(gòu)體只可以簡(jiǎn)單地理解為類(lèi)的前身。
26 簡(jiǎn)述指針常量與常量指針的區(qū)別
- 指針常量是指定義了一個(gè)指針,這個(gè)指針的值只能在定義時(shí)初始化,其他地方不能改變。常量指針是指定義了一個(gè)指針,這個(gè)指針指向一個(gè)只讀的對(duì)象,不能通過(guò)常量指針來(lái)改變這個(gè)對(duì)象的值。
- 指針常量強(qiáng)調(diào)的是指針的不可改變性,而常量指針強(qiáng)調(diào)的是指針對(duì)其所指對(duì)象的不可改變性。
「注意」:無(wú)論是指針常量還是常量指針,其最大的用途就是作為函數(shù)的形式參數(shù),保證實(shí)參在被調(diào)用函數(shù)中的不可改變特性。
27 如何避免“野指針”
- 指針變量聲明時(shí)沒(méi)有被初始化。解決辦法:指針聲明時(shí)初始化,可以是具體的地址值,也可讓它指向NULL。
- 指針p被free或者delete之后,沒(méi)有置為NULL。解決辦法:指針指向的內(nèi)存空間被釋放后指針應(yīng)該指向NULL。
- 指針操作超越了變量的作用范圍。解決辦法:在變量的作用域結(jié)束前釋放掉變量的地址空間并且讓指針指向NULL。
28 句柄和指針的區(qū)別和聯(lián)系是什么?
句柄和指針其實(shí)是兩個(gè)截然不同的概念。Windows系統(tǒng)用句柄標(biāo)記系統(tǒng)資源,隱藏系統(tǒng)的信息。你只要知道有這個(gè)東西,然后去調(diào)用就行了,它是個(gè)32it的uint。指針則標(biāo)記某個(gè)物理內(nèi)存地址,兩者是不同的概念。
29 new/delete與malloc/free的區(qū)別是什么
- new能自動(dòng)計(jì)算需要分配的內(nèi)存空間,而malloc需要手工計(jì)算字節(jié)數(shù)。
int *p = new int[2]; int *q = (int *)malloc(2*sizeof(int));
- new與delete直接帶具體類(lèi)型的指針,malloc和free返回void類(lèi)型的指針。
- new類(lèi)型是安全的,而malloc不是。例如int *p = new float[2];就會(huì)報(bào)錯(cuò);而int p = malloc(2sizeof(int))編譯時(shí)編譯器就無(wú)法指出錯(cuò)誤來(lái)。
- new一般分為兩步:new操作和構(gòu)造。new操作對(duì)應(yīng)與malloc,但new操作可以重載,可以自定義內(nèi)存分配策略,不做內(nèi)存分配,甚至分配到非內(nèi)存設(shè)備上,而malloc不行。
- new調(diào)用構(gòu)造函數(shù),malloc不能;delete調(diào)用析構(gòu)函數(shù),而free不能。
- malloc/free需要庫(kù)文件stdlib.h的支持,new/delete則不需要!
「注意」:delete和free被調(diào)用后,內(nèi)存不會(huì)立即回收,指針也不會(huì)指向空,delete或free僅僅是告訴操作系統(tǒng),這一塊內(nèi)存被釋放了,可以用作其他用途。但是由于沒(méi)有重新對(duì)這塊內(nèi)存進(jìn)行寫(xiě)操作,所以內(nèi)存中的變量數(shù)值并沒(méi)有發(fā)生變化,出現(xiàn)野指針的情況。因此,釋放完內(nèi)存后,應(yīng)該講該指針指向NULL。
30 說(shuō)一說(shuō)extern“C”
extern "C"的主要作用就是為了能夠正確實(shí)現(xiàn)C++代碼調(diào)用其他C語(yǔ)言代碼。加上extern "C"后,會(huì)指示編譯器這部分代碼按C語(yǔ)言(而不是C++)的方式進(jìn)行編譯。由于C++支持函數(shù)重載,因此編譯器編譯函數(shù)的過(guò)程中會(huì)將函數(shù)的參數(shù)類(lèi)型也加到編譯后的代碼中,而不僅僅是函數(shù)名;而C語(yǔ)言并不支持函數(shù)重載,因此編譯C語(yǔ)言代碼的函數(shù)時(shí)不會(huì)帶上函數(shù)的參數(shù)類(lèi)型,一般只包括函數(shù)名。
這個(gè)功能十分有用處,因?yàn)樵贑++出現(xiàn)以前,很多代碼都是C語(yǔ)言寫(xiě)的,而且很底層的庫(kù)也是C語(yǔ)言寫(xiě)的,為了更好的支持原來(lái)的C代碼和已經(jīng)寫(xiě)好的C語(yǔ)言庫(kù),需要在C++中盡可能的支持C,而extern "C"就是其中的一個(gè)策略。
- C++代碼調(diào)用C語(yǔ)言代碼
- 在C++的頭文件中使用
- 在多個(gè)人協(xié)同開(kāi)發(fā)時(shí),可能有的人比較擅長(zhǎng)C語(yǔ)言,而有的人擅長(zhǎng)C++,這樣的情況下也會(huì)有用到
31 請(qǐng)你來(lái)說(shuō)一下C++中struct和class的區(qū)別
在C++中,class和struct做類(lèi)型定義是只有兩點(diǎn)區(qū)別:
- 默認(rèn)繼承權(quán)限不同,class繼承默認(rèn)是private繼承,而struct默認(rèn)是public繼承
- class還可用于定義模板參數(shù),像typename,但是關(guān)鍵字struct不能同于定義模板參數(shù) C++保留struct關(guān)鍵字,原因
- 保證與C語(yǔ)言的向下兼容性,C++必須提供一個(gè)struct
- C++中的struct定義必須百分百地保證與C語(yǔ)言中的struct的向下兼容性,把C++中的最基本的對(duì)象單元規(guī)定為class而不是struct,就是為了避免各種兼容性要求的限制
- 對(duì)struct定義的擴(kuò)展使C語(yǔ)言的代碼能夠更容易的被移植到C++中
32 C++類(lèi)內(nèi)可以定義引用數(shù)據(jù)成員嗎?
可以,必須通過(guò)成員函數(shù)初始化列表初始化。
33 C++中類(lèi)成員的訪問(wèn)權(quán)限
C++通過(guò) public、protected、private 三個(gè)關(guān)鍵字來(lái)控制成員變量和成員函數(shù)的訪問(wèn)權(quán)限,它們分別表示公有的、受保護(hù)的、私有的,被稱(chēng)為成員訪問(wèn)限定符。在類(lèi)的內(nèi)部(定義類(lèi)的代碼內(nèi)部),無(wú)論成員被聲明為 public、protected 還是 private,都是可以互相訪問(wèn)的,沒(méi)有訪問(wèn)權(quán)限的限制。在類(lèi)的外部(定義類(lèi)的代碼之外),只能通過(guò)對(duì)象訪問(wèn)成員,并且通過(guò)對(duì)象只能訪問(wèn) public 屬性的成員,不能訪問(wèn) private、protected 屬性的成員
34 什么是右值引用,跟左值又有什么區(qū)別?
左值和右值的概念:
- 左值:能取地址,或者具名對(duì)象,表達(dá)式結(jié)束后依然存在的持久對(duì)象;
- 右值:不能取地址,匿名對(duì)象,表達(dá)式結(jié)束后就不再存在的臨時(shí)對(duì)象;區(qū)別:
- 左值能賦值,右值不能;
- 左值可變,右值不能(僅對(duì)基礎(chǔ)類(lèi)型適用,用戶自定義類(lèi)型右值引用可以通過(guò)成員函數(shù)改變);
35 面向?qū)ο蟮娜筇卣?/span>
- 封裝性:將客觀事物抽象成類(lèi),每個(gè)類(lèi)對(duì)自身的數(shù)據(jù)和方法實(shí)行 protection (private , protected , public )。
- 繼承性:廣義的繼承有三種實(shí)現(xiàn)形式:實(shí)現(xiàn)繼承(使用基類(lèi)的屬性和方法而無(wú)需額外編碼的能力)、可 視繼承(子窗體使用父窗體的外觀和實(shí)現(xiàn)代碼)、接口繼承(僅使用屬性和方法,實(shí)現(xiàn)滯后到子類(lèi)實(shí)現(xiàn))。
- 多態(tài)性:是將父類(lèi)對(duì)象設(shè)置成為和一個(gè)或更多它的子對(duì)象相等的技術(shù)。用子類(lèi)對(duì)象給父類(lèi)對(duì)象賦值 之后,父類(lèi)對(duì)象就可以根據(jù)當(dāng)前賦值給它的子對(duì)象的特性以不同的方式運(yùn)作。
36 說(shuō)一說(shuō)c++中四種cast轉(zhuǎn)換
C++中四種類(lèi)型轉(zhuǎn)換是:static_cast, dynamic_cast, const_cast, reinterpret_cast
1、const_cast
- 用于將const變量轉(zhuǎn)為非const
2、static_cast
- 用于各種隱式轉(zhuǎn)換,比如非const轉(zhuǎn)const,void*轉(zhuǎn)指針等, static_cast能用于多態(tài)向上轉(zhuǎn)化,如果向下轉(zhuǎn)能成功但是不安全,結(jié)果未知;
3、dynamic_cast
用于動(dòng)態(tài)類(lèi)型轉(zhuǎn)換。只能用于含有虛函數(shù)的類(lèi),用于類(lèi)層次間的向上和向下轉(zhuǎn)化。只能轉(zhuǎn)指針或引用。向下轉(zhuǎn)化時(shí),如果是非法的***對(duì)于指針?lè)祷豊ULL,對(duì)于引用拋異常***。要深入了解內(nèi)部轉(zhuǎn)換的原理。
- 向上轉(zhuǎn)換:指的是子類(lèi)向基類(lèi)的轉(zhuǎn)換
- 向下轉(zhuǎn)換:指的是基類(lèi)向子類(lèi)的轉(zhuǎn)換
它通過(guò)判斷在執(zhí)行到該語(yǔ)句的時(shí)候變量的運(yùn)行時(shí)類(lèi)型和要轉(zhuǎn)換的類(lèi)型是否相同來(lái)判斷是否能夠進(jìn)行向下轉(zhuǎn)換。
4、reinterpret_cast
- 幾乎什么都可以轉(zhuǎn),比如將int轉(zhuǎn)指針,可能會(huì)出問(wèn)題,盡量少用;
5、為什么不使用C的強(qiáng)制轉(zhuǎn)換?
- C的強(qiáng)制轉(zhuǎn)換表面上看起來(lái)功能強(qiáng)大什么都能轉(zhuǎn),但是轉(zhuǎn)化不夠明確,不能進(jìn)行錯(cuò)誤檢查,容易出錯(cuò)。
37 C++的空類(lèi)有哪些成員函數(shù)
- 缺省構(gòu)造函數(shù)。
- 缺省拷貝構(gòu)造函數(shù)。
- 省析構(gòu)函數(shù)。
- 賦值運(yùn)算符。
- 取址運(yùn)算符。
- 取址運(yùn)算符 const 。
「注意」:有些書(shū)上只是簡(jiǎn)單的介紹了前四個(gè)函數(shù)。沒(méi)有提及后面這兩個(gè)函數(shù)。但后面這兩個(gè)函數(shù)也是 空類(lèi)的默認(rèn)函數(shù)。另外需要注意的是,只有當(dāng)實(shí)際使用這些函數(shù)的時(shí)候,編譯器才會(huì)去定義它們。
38 對(duì)c++中的smart pointer四個(gè)智能指針:shared_ptr,unique_ptr,weak_ptr,auto_ptr的理解
C++里面的四個(gè)智能指針: auto_ptr, shared_ptr, weak_ptr, unique_ptr 其中后三個(gè)是c++11支持,并且第一個(gè)已經(jīng)被11棄用。
智能指針的作用是管理一個(gè)指針,因?yàn)榇嬖谝韵逻@種情況:申請(qǐng)的空間在函數(shù)結(jié)束時(shí)忘記釋放,造成內(nèi)存泄漏。使用智能指針可以很大程度上的避免這個(gè)問(wèn)題,因?yàn)橹悄苤羔樉褪且粋€(gè)類(lèi),當(dāng)超出了類(lèi)的作用域是,類(lèi)會(huì)自動(dòng)調(diào)用析構(gòu)函數(shù),析構(gòu)函數(shù)會(huì)自動(dòng)釋放資源。所以智能指針的作用原理就是在函數(shù)結(jié)束時(shí)自動(dòng)釋放內(nèi)存空間,不需要手動(dòng)釋放內(nèi)存空間。
- auto_ptr(c++98的方案,cpp11已經(jīng)拋棄)
采用所有權(quán)模式。
auto_ptr< string> p1 (new string ("I reigned lonely as a cloud.”));
auto_ptrp2;
p2 = p1; //auto_ptr不會(huì)報(bào)錯(cuò).
此時(shí)不會(huì)報(bào)錯(cuò),p2剝奪了p1的所有權(quán),但是當(dāng)程序運(yùn)行時(shí)訪問(wèn)p1將會(huì)報(bào)錯(cuò)。所以auto_ptr的缺點(diǎn)是:存在潛在的內(nèi)存崩潰問(wèn)題!
- unique_ptr(替換auto_ptr)
unique_ptr實(shí)現(xiàn)獨(dú)占式擁有或嚴(yán)格擁有概念,保證同一時(shí)間內(nèi)只有一個(gè)智能指針可以指向該對(duì)象。它對(duì)于避免資源泄露(例如“以new創(chuàng)建對(duì)象后因?yàn)榘l(fā)生異常而忘記調(diào)用delete”)特別有用。
采用所有權(quán)模式。
unique_ptrp3 (new string ("auto")); //#4 unique_ptrp4; //#5 p4 = p3;//此時(shí)會(huì)報(bào)錯(cuò)??!
編譯器認(rèn)為p4=p3非法,避免了p3不再指向有效數(shù)據(jù)的問(wèn)題。因此,unique_ptr比auto_ptr更安全。
另外unique_ptr還有更聰明的地方:當(dāng)程序試圖將一個(gè) unique_ptr 賦值給另一個(gè)時(shí),如果源 unique_ptr 是個(gè)臨時(shí)右值,編譯器允許這么做;如果源 unique_ptr 將存在一段時(shí)間,編譯器將禁止這么做,比如:
unique_ptrpu1(new string ("hello world")); unique_ptrpu2; pu2 = pu1; // #1 not allowed unique_ptrpu3; pu3 = unique_ptr(new string ("You")); // #2 allowed
其中#1留下懸掛的unique_ptr(pu1),這可能導(dǎo)致危害。而#2不會(huì)留下懸掛的unique_ptr,因?yàn)樗{(diào)用 unique_ptr 的構(gòu)造函數(shù),該構(gòu)造函數(shù)創(chuàng)建的臨時(shí)對(duì)象在其所有權(quán)讓給 pu3 后就會(huì)被銷(xiāo)毀。這種隨情況而已的行為表明,unique_ptr 優(yōu)于允許兩種賦值的auto_ptr 。
「注意」:如果確實(shí)想執(zhí)行類(lèi)似與#1的操作,要安全的重用這種指針,可給它賦新值。C++有一個(gè)標(biāo)準(zhǔn)庫(kù)函數(shù)std::move(),讓你能夠?qū)⒁粋€(gè)unique_ptr賦給另一個(gè)。例如:
unique_ptrps1, ps2; ps1 = demo("hello"); ps2 = move(ps1); ps1 = demo("alexia"); cout << *ps2 << *ps1 << endl;
- shared_ptr
shared_ptr實(shí)現(xiàn)共享式擁有概念。多個(gè)智能指針可以指向相同對(duì)象,該對(duì)象和其相關(guān)資源會(huì)在“最后一個(gè)引用被銷(xiāo)毀”時(shí)候釋放。從名字share就可以看出了資源可以被多個(gè)指針共享,它使用計(jì)數(shù)機(jī)制來(lái)表明資源被幾個(gè)指針共享??梢酝ㄟ^(guò)成員函數(shù)use_count()來(lái)查看資源的所有者個(gè)數(shù)。除了可以通過(guò)new來(lái)構(gòu)造,還可以通過(guò)傳入auto_ptr, unique_ptr,weak_ptr來(lái)構(gòu)造。當(dāng)我們調(diào)用release()時(shí),當(dāng)前指針會(huì)釋放資源所有權(quán),計(jì)數(shù)減一。當(dāng)計(jì)數(shù)等于0時(shí),資源會(huì)被釋放。
shared_ptr 是為了解決 auto_ptr 在對(duì)象所有權(quán)上的局限性(auto_ptr 是獨(dú)占的), 在使用引用計(jì)數(shù)的機(jī)制上提供了可以共享所有權(quán)的智能指針。
成員函數(shù):
use_count 返回引用計(jì)數(shù)的個(gè)數(shù)
unique 返回是否是獨(dú)占所有權(quán)( use_count 為 1)
swap 交換兩個(gè) shared_ptr 對(duì)象(即交換所擁有的對(duì)象)
reset 放棄內(nèi)部對(duì)象的所有權(quán)或擁有對(duì)象的變更, 會(huì)引起原有對(duì)象的引用計(jì)數(shù)的減少
get 返回內(nèi)部對(duì)象(指針), 由于已經(jīng)重載了()方法, 因此和直接使用對(duì)象是一樣的.如 shared_ptrsp(new int(1)); sp 與 sp.get()是等價(jià)的
- weak_ptr
weak_ptr 是一種不控制對(duì)象生命周期的智能指針, 它指向一個(gè) shared_ptr 管理的對(duì)象. 進(jìn)行該對(duì)象的內(nèi)存管理的是那個(gè)強(qiáng)引用的 shared_ptr. weak_ptr只是提供了對(duì)管理對(duì)象的一個(gè)訪問(wèn)手段。weak_ptr 設(shè)計(jì)的目的是為配合 shared_ptr 而引入的一種智能指針來(lái)協(xié)助 shared_ptr 工作, 它只可以從一個(gè) shared_ptr 或另一個(gè) weak_ptr 對(duì)象構(gòu)造, 它的構(gòu)造和析構(gòu)不會(huì)引起引用記數(shù)的增加或減少。weak_ptr是用來(lái)解決shared_ptr相互引用時(shí)的死鎖問(wèn)題,如果說(shuō)兩個(gè)shared_ptr相互引用,那么這兩個(gè)指針的引用計(jì)數(shù)永遠(yuǎn)不可能下降為0,資源永遠(yuǎn)不會(huì)釋放。它是對(duì)對(duì)象的一種弱引用,不會(huì)增加對(duì)象的引用計(jì)數(shù),和shared_ptr之間可以相互轉(zhuǎn)化,shared_ptr可以直接賦值給它,它可以通過(guò)調(diào)用lock函數(shù)來(lái)獲得shared_ptr。
class B; class A { public: shared_ptr pb_; ~A() { cout<<"A delete "; } }; class B { public: shared_ptr pa_; ~B() { cout<<"B delete "; } }; void fun() { shared_ptr pb(new B()); shared_ptr pa(new A()); pb->pa_ = pa; pa->pb_ = pb; cout<main() { fun(); return 0; }
可以看到fun函數(shù)中pa ,pb之間互相引用,兩個(gè)資源的引用計(jì)數(shù)為2,當(dāng)要跳出函數(shù)時(shí),智能指針pa,pb析構(gòu)時(shí)兩個(gè)資源引用計(jì)數(shù)會(huì)減一,但是兩者引用計(jì)數(shù)還是為1,導(dǎo)致跳出函數(shù)時(shí)資源沒(méi)有被釋放(A B的析構(gòu)函數(shù)沒(méi)有被調(diào)用),如果把其中一個(gè)改為weak_ptr就可以了,我們把類(lèi)A里面的shared_ptr pb_; 改為weak_ptr pb_; 運(yùn)行結(jié)果如下,這樣的話,資源B的引用開(kāi)始就只有1,當(dāng)pb析構(gòu)時(shí),B的計(jì)數(shù)變?yōu)?,B得到釋放,B釋放的同時(shí)也會(huì)使A的計(jì)數(shù)減一,同時(shí)pa析構(gòu)時(shí)使A的計(jì)數(shù)減一,那么A的計(jì)數(shù)為0,A得到釋放。
「注意」:不能通過(guò)weak_ptr直接訪問(wèn)對(duì)象的方法,比如B對(duì)象中有一個(gè)方法print(),我們不能這樣訪問(wèn),pa->pb_->print(); 英文pb_是一個(gè)weak_ptr,應(yīng)該先把它轉(zhuǎn)化為shared_ptr,如:shared_ptr p = pa->pb_.lock(); p->print();
39 說(shuō)說(shuō)強(qiáng)制類(lèi)型轉(zhuǎn)換運(yùn)算符
「static_cast」
- 用于非多態(tài)類(lèi)型的轉(zhuǎn)換
- 不執(zhí)行運(yùn)行時(shí)類(lèi)型檢查(轉(zhuǎn)換安全性不如 dynamic_cast)
- 通常用于轉(zhuǎn)換數(shù)值數(shù)據(jù)類(lèi)型(如 float -> int)
- 可以在整個(gè)類(lèi)層次結(jié)構(gòu)中移動(dòng)指針,子類(lèi)轉(zhuǎn)化為父類(lèi)安全(向上轉(zhuǎn)換),父類(lèi)轉(zhuǎn)化為子類(lèi)不安全(因?yàn)樽宇?lèi)可能有不在父類(lèi)的字段或方法)
「dynamic_cast」
- 用于多態(tài)類(lèi)型的轉(zhuǎn)換
- 執(zhí)行行運(yùn)行時(shí)類(lèi)型檢查
- 只適用于指針或引用
- 對(duì)不明確的指針的轉(zhuǎn)換將失?。ǚ祷?nullptr),但不引發(fā)異常
- 可以在整個(gè)類(lèi)層次結(jié)構(gòu)中移動(dòng)指針,包括向上轉(zhuǎn)換、向下轉(zhuǎn)換
「const_cast」
- 用于刪除 const、volatile 和 __unaligned 特性(如將 const int 類(lèi)型轉(zhuǎn)換為 int 類(lèi)型 ) reinterpret_cast
- 用于位的簡(jiǎn)單重新解釋
- 濫用 reinterpret_cast 運(yùn)算符可能很容易帶來(lái)風(fēng)險(xiǎn)。除非所需轉(zhuǎn)換本身是低級(jí)別的,否則應(yīng)- 使用其他強(qiáng)制轉(zhuǎn)換運(yùn)算符之一。
- 允許將任何指針轉(zhuǎn)換為任何其他指針類(lèi)型(如 char* 到 int* 或 One_class* 到 Unrelated_class* 之類(lèi)的轉(zhuǎn)換,但其本身并不安全)
- 也允許將任何整數(shù)類(lèi)型轉(zhuǎn)換為任何指針類(lèi)型以及反向轉(zhuǎn)換。
- reinterpret_cast 運(yùn)算符不能丟掉 const、volatile 或 __unaligned 特性。
- reinterpret_cast 的一個(gè)實(shí)際用途是在哈希函數(shù)中,即,通過(guò)讓兩個(gè)不同的值幾乎不以相同的索引結(jié)尾的方式將值映射到索引。
「bad_cast」
- 由于強(qiáng)制轉(zhuǎn)換為引用類(lèi)型失敗,dynamic_cast 運(yùn)算符引發(fā) bad_cast 異常。
bad_cast 使用
try { Circle& ref_circle = dynamic_cast(ref_shape); } catch (bad_cast b) { cout << "Caught: " << b.what(); }
40 談?wù)勀銓?duì)拷貝構(gòu)造函數(shù)和賦值運(yùn)算符的認(rèn)識(shí)
拷貝構(gòu)造函數(shù)和賦值運(yùn)算符重載有以下兩個(gè)不同之處:
- 拷貝構(gòu)造函數(shù)生成新的類(lèi)對(duì)象,而賦值運(yùn)算符不能。
- 由于拷貝構(gòu)造函數(shù)是直接構(gòu)造一個(gè)新的類(lèi)對(duì)象,所以在初始化這個(gè)對(duì)象之前不用檢驗(yàn)源對(duì)象 是否和新建對(duì)象相同。而賦值運(yùn)算符則需要這個(gè)操作,另外賦值運(yùn)算中如果原來(lái)的對(duì)象中有內(nèi)存分配要先把內(nèi)存釋放掉。
「注意」:當(dāng)有類(lèi)中有指針類(lèi)型的成員變量時(shí),一定要重寫(xiě)拷貝構(gòu)造函數(shù)和賦值運(yùn)算符,不要使用默認(rèn) 的。
41 在C++中,使用malloc申請(qǐng)的內(nèi)存能否通過(guò)delete釋放?使用new申請(qǐng)的內(nèi)存能否用free?
不能,malloc /free主要為了兼容C,new和delete 完全可以取代malloc /free的。malloc /free的操作對(duì)象都是必須明確大小的。而且不能用在動(dòng)態(tài)類(lèi)上。new 和delete會(huì)自動(dòng)進(jìn)行類(lèi)型檢查和大小,malloc/free不能執(zhí)行構(gòu)造函數(shù)與析構(gòu)函數(shù),所以動(dòng)態(tài)對(duì)象它是不行的。當(dāng)然從理論上說(shuō)使用malloc申請(qǐng)的內(nèi)存是可以通過(guò)delete釋放的。不過(guò)一般不這樣寫(xiě)的。而且也不能保證每個(gè)C++的運(yùn)行時(shí)都能正常。
42 用C++設(shè)計(jì)一個(gè)不能被繼承的類(lèi)
templateclass A { friend T; private: A() {} ~A() {} }; class B : virtual public A { public: B() {} ~B() {} }; class C : virtual public B { public: C() {} ~C() {} }; void main( void ) { B b; //C c; return; }
「注意」:構(gòu)造函數(shù)是繼承實(shí)現(xiàn)的關(guān)鍵,每次子類(lèi)對(duì)象構(gòu)造時(shí),首先調(diào)用的是父類(lèi)的構(gòu)造函數(shù),然后才 是自己的。
43 C++自己實(shí)現(xiàn)一個(gè)String類(lèi)
#include #include using namespace std; class String{ public: // 默認(rèn)構(gòu)造函數(shù) String(const char *str = nullptr); // 拷貝構(gòu)造函數(shù) String(const String &str); // 析構(gòu)函數(shù) ~String(); // 字符串賦值函數(shù) String& operator=(const String &str); private: char *m_data; int m_size; }; // 構(gòu)造函數(shù) String::String(const char *str) { if(str == nullptr) // 加分點(diǎn):對(duì)m_data加NULL 判斷 { m_data = new char[1]; // 得分點(diǎn):對(duì)空字符串自動(dòng)申請(qǐng)存放結(jié)束標(biāo)志'\0'的 m_data[0] = '\0'; m_size = 0; } else { m_size = strlen(str); m_data = new char[m_size + 1]; strcpy(m_data, str); } } // 拷貝構(gòu)造函數(shù) String::String(const String &str) // 得分點(diǎn):輸入?yún)?shù)為const型 { m_size = str.m_size; m_data = new char[m_size + 1]; //加分點(diǎn):對(duì)m_data加NULL 判斷 strcpy(m_data, str.m_data); } // 析構(gòu)函數(shù) String::~String() { delete[] m_data; } // 字符串賦值函數(shù) String& String::operator=(const String &str) // 得分點(diǎn):輸入?yún)?shù)為const { if(this == &str) //得分點(diǎn):檢查自賦值 return *this; delete[] m_data; //得分點(diǎn):釋放原有的內(nèi)存資源 m_size = strlen(str.m_data); m_data = new char[m_size + 1]; //加分點(diǎn):對(duì)m_data加NULL 判斷 strcpy(m_data, str.m_data); return *this; //得分點(diǎn):返回本對(duì)象的引用 }
44 訪問(wèn)基類(lèi)的私有虛函數(shù)
寫(xiě)出以下程序的輸出結(jié)果:
#includeclass A { virtual void g() { cout << "A::g" << endl; } private: virtual void f() { cout << "A::f" << endl; } }; class B : public A { void g() { cout << "B::g" << endl; } virtual void h() { cout << "B::h" << endl; } }; typedef void( *Fun )( void ); void main() { B b; Fun pFun; for(int i = 0 ; i < 3; i++) { pFun = ( Fun )*( ( int* ) * ( int* )( &b ) + i ); pFun(); } }
輸出結(jié)果:
B::g A::f B::h
「注意」:考察了面試者對(duì)虛函數(shù)的理解程度。一個(gè)對(duì)虛函數(shù)不了解的人很難正確的做出本題。在學(xué)習(xí)面向?qū)ο蟮亩鄳B(tài)性時(shí)一定要深刻理解虛函數(shù)表的工作原理。
45 對(duì)虛函數(shù)和多態(tài)的理解
多態(tài)的實(shí)現(xiàn)主要分為靜態(tài)多態(tài)和動(dòng)態(tài)多態(tài),靜態(tài)多態(tài)主要是重載,在編譯的時(shí)候就已經(jīng)確定;動(dòng)態(tài)多態(tài)是用虛函數(shù)機(jī)制實(shí)現(xiàn)的,在運(yùn)行期間動(dòng)態(tài)綁定。舉個(gè)例子:一個(gè)父類(lèi)類(lèi)型的指針指向一個(gè)子類(lèi)對(duì)象時(shí)候,使用父類(lèi)的指針去調(diào)用子類(lèi)中重寫(xiě)了的父類(lèi)中的虛函數(shù)的時(shí)候,會(huì)調(diào)用子類(lèi)重寫(xiě)過(guò)后的函數(shù),在父類(lèi)中聲明為加了virtual關(guān)鍵字的函數(shù),在子類(lèi)中重寫(xiě)時(shí)候不需要加virtual也是虛函數(shù)。
虛函數(shù)的實(shí)現(xiàn):在有虛函數(shù)的類(lèi)中,類(lèi)的最開(kāi)始部分是一個(gè)虛函數(shù)表的指針,這個(gè)指針指向一個(gè)虛函數(shù)表,表中放了虛函數(shù)的地址,實(shí)際的虛函數(shù)在代碼段(.text)中。當(dāng)子類(lèi)繼承了父類(lèi)的時(shí)候也會(huì)繼承其虛函數(shù)表,當(dāng)子類(lèi)重寫(xiě)父類(lèi)中虛函數(shù)時(shí)候,會(huì)將其繼承到的虛函數(shù)表中的地址替換為重新寫(xiě)的函數(shù)地址。使用了虛函數(shù),會(huì)增加訪問(wèn)內(nèi)存開(kāi)銷(xiāo),降低效率。
46 簡(jiǎn)述類(lèi)成員函數(shù)的重寫(xiě)、重載和隱藏的區(qū)別
(1)重寫(xiě)和重載主要有以下幾點(diǎn)不同。
- 范圍的區(qū)別:被重寫(xiě)的和重寫(xiě)的函數(shù)在兩個(gè)類(lèi)中,而重載和被重載的函數(shù)在同一個(gè)類(lèi)中。
- 參數(shù)的區(qū)別:被重寫(xiě)函數(shù)和重寫(xiě)函數(shù)的參數(shù)列表一定相同,而被重載函數(shù)和重載函數(shù)的參數(shù)列表一 定不同。
- virtual 的區(qū)別:重寫(xiě)的基類(lèi)中被重寫(xiě)的函數(shù)必須要有virtual 修飾,而重載函數(shù)和被重載函數(shù)可以被 virtual 修飾,也可以沒(méi)有。
(2)隱藏和重寫(xiě)、重載有以下幾點(diǎn)不同。
- 與重載的范圍不同:和重寫(xiě)一樣,隱藏函數(shù)和被隱藏函數(shù)不在同一個(gè)類(lèi)中。
- 參數(shù)的區(qū)別:隱藏函數(shù)和被隱藏的函數(shù)的參數(shù)列表可以相同,也可不同,但是函數(shù)名肯定要相同。當(dāng)參數(shù)不相同時(shí),無(wú)論基類(lèi)中的參數(shù)是否被virtual 修飾,基類(lèi)的函數(shù)都是被隱藏,而不是被重寫(xiě)。
「注意」:雖然重載和覆蓋都是實(shí)現(xiàn)多態(tài)的基礎(chǔ),但是兩者實(shí)現(xiàn)的技術(shù)完全不相同,達(dá)到的目的也是完 全不同的,覆蓋是動(dòng)態(tài)態(tài)綁定的多態(tài),而重載是靜態(tài)綁定的多態(tài)。
47 鏈表和數(shù)組有什么區(qū)別
- 存儲(chǔ)形式:數(shù)組是一塊連續(xù)的空間,聲明時(shí)就要確定長(zhǎng)度。鏈表是一塊可不連續(xù)的動(dòng)態(tài)空間, 長(zhǎng)度可變,每個(gè)結(jié)點(diǎn)要保存相鄰結(jié)點(diǎn)指針。
- 數(shù)據(jù)查找:數(shù)組的線性查找速度快,查找操作直接使用偏移地址。鏈表需要按順序檢索結(jié)點(diǎn), 效率低。
- 數(shù)據(jù)插入或刪除:鏈表可以快速插入和刪除結(jié)點(diǎn),而數(shù)組則可能需要大量數(shù)據(jù)移動(dòng)。
- 越界問(wèn)題:鏈表不存在越界問(wèn)題,數(shù)組有越界問(wèn)題。
「注意」:在選擇數(shù)組或鏈表數(shù)據(jù)結(jié)構(gòu)時(shí),一定要根據(jù)實(shí)際需要進(jìn)行選擇。數(shù)組便于查詢,鏈表便于插 入刪除。數(shù)組節(jié)省空間但是長(zhǎng)度固定,鏈表雖然變長(zhǎng)但是占了更多的存儲(chǔ)空間。
48 用兩個(gè)棧實(shí)現(xiàn)一個(gè)隊(duì)列的功能
typedef struct node { int data; node *next; }node,*LinkStack; //創(chuàng)建空棧: LinkStack CreateNULLStack( LinkStack &S) { S = (LinkStack)malloc( sizeof( node ) ); // 申請(qǐng)新結(jié)點(diǎn) if( NULL == S) { printf("Fail to malloc a new node.\n"); return NULL; } S->data = 0; //初始化新結(jié)點(diǎn) S->next = NULL; return S; } //棧的插入函數(shù): LinkStack Push( LinkStack &S, int data) { if( NULL == S) //檢驗(yàn)棧 { printf("There no node in stack!"); return NULL; } LinkStack p = NULL; p = (LinkStack)malloc( sizeof( node ) ); // 申請(qǐng)新結(jié)點(diǎn) if( NULL == p) { printf("Fail to malloc a new node.\n"); return S; } if( NULL == S->next) { p->next = NULL; } else { p->next = S->next; } p->data = data; //初始化新結(jié)點(diǎn) S->next = p; //插入新結(jié)點(diǎn) return S; } //出棧函數(shù): node Pop( LinkStack &S) { node temp; temp.data = 0; temp.next = NULL; if( NULL == S) //檢驗(yàn)棧 { printf("There no node in stack!"); return temp; } temp = *S; if( S->next == NULL ) { printf("The stack is NULL,can't pop!\n"); return temp; } LinkStack p = S ->next; //節(jié)點(diǎn)出棧 S->next = S->next->next; temp = *p; free( p ); p = NULL; return temp; } //雙棧實(shí)現(xiàn)隊(duì)列的入隊(duì)函數(shù): LinkStack StackToQueuPush( LinkStack &S, int data) { node n; LinkStack S1 = NULL; CreateNULLStack( S1 ); //創(chuàng)建空棧 while( NULL != S->next ) //S 出棧入S1 { n = Pop( S ); Push( S1, n.data ); } Push( S1, data ); //新結(jié)點(diǎn)入棧 while( NULL != S1->next ) //S1 出棧入S { n = Pop( S1 ); Push( S, n.data ); } return S; }
「注意」:用兩個(gè)棧能夠?qū)崿F(xiàn)一個(gè)隊(duì)列的功能,那用兩個(gè)隊(duì)列能否實(shí)現(xiàn)一個(gè)隊(duì)列的功能呢?結(jié)果是否定 的,因?yàn)闂J窍冗M(jìn)后出,將兩個(gè)棧連在一起,就是先進(jìn)先出。而隊(duì)列是現(xiàn)先進(jìn)先出,無(wú)論多少個(gè)連在一 起都是先進(jìn)先出,而無(wú)法實(shí)現(xiàn)先進(jìn)后出。
49 模板函數(shù)和模板類(lèi)的特例化
「引入原因」
編寫(xiě)單一的模板,它能適應(yīng)多種類(lèi)型的需求,使每種類(lèi)型都具有相同的功能,但對(duì)于某種特定類(lèi)型,如果要實(shí)現(xiàn)其特有的功能,單一模板就無(wú)法做到,這時(shí)就需要模板特例化
「定義」對(duì)單一模板提供的一個(gè)特殊實(shí)例,它將一個(gè)或多個(gè)模板參數(shù)綁定到特定的類(lèi)型或值上
(1)模板函數(shù)特例化
必須為原函數(shù)模板的每個(gè)模板參數(shù)都提供實(shí)參,且使用關(guān)鍵字template后跟一個(gè)空尖括號(hào)對(duì)<>,表明將原模板的所有模板參數(shù)提供實(shí)參,舉例如下:
template//模板函數(shù) int compare(const T &v1,const T &v2) { if(v1 > v2) return -1; if(v2 > v1) return 1; return 0; } //模板特例化,滿足針對(duì)字符串特定的比較,要提供所有實(shí)參,這里只有一個(gè)T template<> int compare(const char* const &v1,const char* const &v2) { return strcmp(p1,p2); }
「本質(zhì)」特例化的本質(zhì)是實(shí)例化一個(gè)模板,而非重載它。特例化不影響參數(shù)匹配。參數(shù)匹配都以最佳匹配為原則。例如,此處如果是compare(3,5),則調(diào)用普通的模板,若為compare(“hi”,”haha”)則調(diào)用特例化版本(因?yàn)檫@個(gè)cosnt char*相對(duì)于T,更匹配實(shí)參類(lèi)型),注意二者函數(shù)體的語(yǔ)句不一樣了,實(shí)現(xiàn)不同功能。
「注意」模板及其特例化版本應(yīng)該聲明在同一個(gè)頭文件中,且所有同名模板的聲明應(yīng)該放在前面,后面放特例化版本。
(2)類(lèi)模板特例化
原理類(lèi)似函數(shù)模板,不過(guò)在類(lèi)中,我們可以對(duì)模板進(jìn)行特例化,也可以對(duì)類(lèi)進(jìn)行部分特例化。對(duì)類(lèi)進(jìn)行特例化時(shí),仍然用template<>表示是一個(gè)特例化版本,例如:
template<>
class hash{
size_t operator()(sales_data& s);
//里面所有T都換成特例化類(lèi)型版本sales_data
//按照最佳匹配原則,若T != sales_data,就用普通類(lèi)模板,否則,就使用含有特定功能的特例化版本。
};
「類(lèi)模板的部分特例化」
不必為所有模板參數(shù)提供實(shí)參,可以指定一部分而非所有模板參數(shù),一個(gè)類(lèi)模板的部分特例化本身仍是一個(gè)模板,使用它時(shí)還必須為其特例化版本中未指定的模板參數(shù)提供實(shí)參(特例化時(shí)類(lèi)名一定要和原來(lái)的模板相同,只是參數(shù)類(lèi)型不同,按最佳匹配原則,哪個(gè)最匹配,就用相應(yīng)的模板)
「特例化類(lèi)中的部分成員」
可以特例化類(lèi)中的部分成員函數(shù)而不是整個(gè)類(lèi),舉個(gè)例子:
templateclass Foo { void Bar(); void Barst(T a)(); }; template<> void Foo::Bar() { //進(jìn)行int類(lèi)型的特例化處理 cout << "我是int型特例化" << endl; } Foofs; Foofi;//使用特例化 fs.Bar();//使用的是普通模板,即Foo::Bar() fi.Bar();//特例化版本,執(zhí)行Foo::Bar() //Foo::Bar()和Foo::Bar()功能不同
50 為什么析構(gòu)函數(shù)一般寫(xiě)成虛函數(shù)
由于類(lèi)的多態(tài)性,基類(lèi)指針可以指向派生類(lèi)的對(duì)象,如果刪除該基類(lèi)的指針,就會(huì)調(diào)用該指針指向的派生類(lèi)析構(gòu)函數(shù),而派生類(lèi)的析構(gòu)函數(shù)又自動(dòng)調(diào)用基類(lèi)的析構(gòu)函數(shù),這樣整個(gè)派生類(lèi)的對(duì)象完全被釋放。如果析構(gòu)函數(shù)不被聲明成虛函數(shù),則編譯器實(shí)施靜態(tài)綁定,在刪除基類(lèi)指針時(shí),只會(huì)調(diào)用基類(lèi)的析構(gòu)函數(shù)而不調(diào)用派生類(lèi)析構(gòu)函數(shù),這樣就會(huì)造成派生類(lèi)對(duì)象析構(gòu)不完全,造成內(nèi)存泄漏。所以將析構(gòu)函數(shù)聲明為虛函數(shù)是十分必要的。在實(shí)現(xiàn)多態(tài)時(shí),當(dāng)用基類(lèi)操作派生類(lèi),在析構(gòu)時(shí)防止只析構(gòu)基類(lèi)而不析構(gòu)派生類(lèi)的狀況發(fā)生,要將基類(lèi)的析構(gòu)函數(shù)聲明為虛函數(shù)。舉個(gè)例子:
#include using namespace std; class Parent{ public: Parent(){ cout << "Parent construct function" << endl; }; ~Parent(){ cout << "Parent destructor function" <Son(){ cout << "Son construct function" << endl; }; ~Son(){ cout << "Son destructor function" < main() { Parent* p = new Son(); delete p; p = NULL; return 0; } //運(yùn)行結(jié)果: //Parent construct function //Son construct function //Parent destructor function
將基類(lèi)的析構(gòu)函數(shù)聲明為虛函數(shù):
#include using namespace std; class Parent{ public: Parent(){ cout << "Parent construct function" << endl; }; virtual ~Parent(){ cout << "Parent destructor function" <Son(){ cout << "Son construct function" << endl; }; ~Son(){ cout << "Son destructor function" < main() { Parent* p = new Son(); delete p; p = NULL; return 0; } //運(yùn)行結(jié)果: //Parent construct function //Son construct function //Son destructor function //Parent destructor function
51 vector的底層原理
vector底層是一個(gè)動(dòng)態(tài)數(shù)組,包含三個(gè)迭代器,start和finish之間是已經(jīng)被使用的空間范圍,end_of_storage是整塊連續(xù)空間包括備用空間的尾部。
當(dāng)空間不夠裝下數(shù)據(jù)(vec.push_back(val))時(shí),會(huì)自動(dòng)申請(qǐng)另一片更大的空間(1.5倍或者2倍),然后把原來(lái)的數(shù)據(jù)拷貝到新的內(nèi)存空間,接著釋放原來(lái)的那片空間[vector內(nèi)存增長(zhǎng)機(jī)制]。
當(dāng)釋放或者刪除(vec.clear())里面的數(shù)據(jù)時(shí),其存儲(chǔ)空間不釋放,僅僅是清空了里面的數(shù)據(jù)。因此,對(duì)vector的任何操作一旦引起了空間的重新配置,指向原vector的所有迭代器會(huì)都失效了。
52 vector中的reserve和resize的區(qū)別
- reserve是直接擴(kuò)充到已經(jīng)確定的大小,可以減少多次開(kāi)辟、釋放空間的問(wèn)題(優(yōu)化push_back),就可以提高效率,其次還可以減少多次要拷貝數(shù)據(jù)的問(wèn)題。reserve只是保證vector中的空間大?。╟apacity)最少達(dá)到參數(shù)所指定的大小n。reserve()只有一個(gè)參數(shù)。
- resize()可以改變有效空間的大小,也有改變默認(rèn)值的功能。capacity的大小也會(huì)隨著改變。resize()可以有多個(gè)參數(shù)。
53 vector中的size和capacity的區(qū)別
- size表示當(dāng)前vector中有多少個(gè)元素(finish - start);
- capacity函數(shù)則表示它已經(jīng)分配的內(nèi)存中可以容納多少元素(end_of_storage - start);
54 vector中erase方法與algorithn中的remove方法區(qū)別
- vector中erase方法真正刪除了元素,迭代器不能訪問(wèn)了
- remove只是簡(jiǎn)單地將元素移到了容器的最后面,迭代器還是可以訪問(wèn)到。因?yàn)閍lgorithm通過(guò)迭代器進(jìn)行操作,不知道容器的內(nèi)部結(jié)構(gòu),所以無(wú)法進(jìn)行真正的刪除。
55 vector迭代器失效的情況
- 當(dāng)插入一個(gè)元素到vector中,由于引起了內(nèi)存重新分配,所以指向原內(nèi)存的迭代器全部失效。
- 當(dāng)刪除容器中一個(gè)元素后,該迭代器所指向的元素已經(jīng)被刪除,那么也造成迭代器失效。erase方法會(huì)返回下一個(gè)有效的迭代器,所以當(dāng)我們要?jiǎng)h除某個(gè)元素時(shí),需要it=vec.erase(it);。
56 正確釋放vector的內(nèi)存(clear(), swap(), shrink_to_fit())
- vec.clear():清空內(nèi)容,但是不釋放內(nèi)存。
- vector().swap(vec):清空內(nèi)容,且釋放內(nèi)存,想得到一個(gè)全新的vector。
- vec.shrink_to_fit():請(qǐng)求容器降低其capacity和size匹配。
- vec.clear();vec.shrink_to_fit();:清空內(nèi)容,且釋放內(nèi)存。
57 list的底層原理
- ist的底層是一個(gè)雙向鏈表,使用鏈表存儲(chǔ)數(shù)據(jù),并不會(huì)將它們存儲(chǔ)到一整塊連續(xù)的內(nèi)存空間中。恰恰相反,各元素占用的存儲(chǔ)空間(又稱(chēng)為節(jié)點(diǎn))是獨(dú)立的、分散的,它們之間的線性關(guān)系通過(guò)指針來(lái)維持,每次插入或刪除一個(gè)元素,就配置或釋放一個(gè)元素空間。
- list不支持隨機(jī)存取,如果需要大量的插入和刪除,而不關(guān)心隨即存取
58 什么情況下用vector,什么情況下用list,什么情況下用deque
- vector可以隨機(jī)存儲(chǔ)元素(即可以通過(guò)公式直接計(jì)算出元素地址,而不需要挨個(gè)查找),但在非尾部插入刪除數(shù)據(jù)時(shí),效率很低,適合對(duì)象簡(jiǎn)單,對(duì)象數(shù)量變化不大,隨機(jī)訪問(wèn)頻繁。除非必要,我們盡可能選擇使用vector而非deque,因?yàn)閐eque的迭代器比vector迭代器復(fù)雜很多。
- list不支持隨機(jī)存儲(chǔ),適用于對(duì)象大,對(duì)象數(shù)量變化頻繁,插入和刪除頻繁,比如寫(xiě)多讀少的場(chǎng)景。
- 需要從首尾兩端進(jìn)行插入或刪除操作的時(shí)候需要選擇deque。
59 priority_queue的底層原理
priority_queue:優(yōu)先隊(duì)列,其底層是用堆來(lái)實(shí)現(xiàn)的。在優(yōu)先隊(duì)列中,隊(duì)首元素一定是當(dāng)前隊(duì)列中優(yōu)先級(jí)最高的那一個(gè)。
60 map 、set、multiset、multimap的底層原理
map 、set、multiset、multimap的底層實(shí)現(xiàn)都是紅黑樹(shù),epoll模型的底層數(shù)據(jù)結(jié)構(gòu)也是紅黑樹(shù),linux系統(tǒng)中CFS進(jìn)程調(diào)度算法,也用到紅黑樹(shù)。
紅黑樹(shù)的特性:
- 每個(gè)結(jié)點(diǎn)或是紅色或是黑色;
- 根結(jié)點(diǎn)是黑色;
- 每個(gè)葉結(jié)點(diǎn)是黑的;
- 如果一個(gè)結(jié)點(diǎn)是紅的,則它的兩個(gè)兒子均是黑色;
- 每個(gè)結(jié)點(diǎn)到其子孫結(jié)點(diǎn)的所有路徑上包含相同數(shù)目的黑色結(jié)點(diǎn)。
61 為何map和set的插入刪除效率比其他序列容器高
因?yàn)椴恍枰獌?nèi)存拷貝和內(nèi)存移動(dòng)
62 為何map和set每次Insert之后,以前保存的iterator不會(huì)失效?
因?yàn)椴迦氩僮髦皇墙Y(jié)點(diǎn)指針換來(lái)?yè)Q去,結(jié)點(diǎn)內(nèi)存沒(méi)有改變。而iterator就像指向結(jié)點(diǎn)的指針,內(nèi)存沒(méi)變,指向內(nèi)存的指針也不會(huì)變。
63 當(dāng)數(shù)據(jù)元素增多時(shí)(從10000到20000),map的set的查找速度會(huì)怎樣變化?
RB-TREE用二分查找法,時(shí)間復(fù)雜度為logn,所以從10000增到20000時(shí),查找次數(shù)從log10000=14次到log20000=15次,多了1次而已。
64 map 、set、multiset、multimap的特點(diǎn)
- set和multiset會(huì)根據(jù)特定的排序準(zhǔn)則自動(dòng)將元素排序,set中元素不允許重復(fù),multiset可以重復(fù)。
- map和multimap將key和value組成的pair作為元素,根據(jù)key的排序準(zhǔn)則自動(dòng)將元素排序(因?yàn)榧t黑樹(shù)也是二叉搜索樹(shù),所以map默認(rèn)是按key排序的),map中元素的key不允許重復(fù),multimap可以重復(fù)。
- map和set的增刪改查速度為都是logn,是比較高效的。
65 為何map和set的插入刪除效率比其他序列容器高,而且每次insert之后,以前保存的iterator不會(huì)失效?
- 存儲(chǔ)的是結(jié)點(diǎn),不需要內(nèi)存拷貝和內(nèi)存移動(dòng)。
- 插入操作只是結(jié)點(diǎn)指針換來(lái)?yè)Q去,結(jié)點(diǎn)內(nèi)存沒(méi)有改變。而iterator就像指向結(jié)點(diǎn)的指針,內(nèi)存沒(méi)變,指向內(nèi)存的指針也不會(huì)變。
66 為何map和set不能像vector一樣有個(gè)reserve函數(shù)來(lái)預(yù)分配數(shù)據(jù)?
在map和set內(nèi)部存儲(chǔ)的已經(jīng)不是元素本身了,而是包含元素的結(jié)點(diǎn)。也就是說(shuō)map內(nèi)部使用的Alloc并不是map
67 set的底層實(shí)現(xiàn)實(shí)現(xiàn)為什么不用哈希表而使用紅黑樹(shù)?
set中元素是經(jīng)過(guò)排序的,紅黑樹(shù)也是有序的,哈希是無(wú)序的 如果只是單純的查找元素的話,那么肯定要選哈希表了,因?yàn)楣1碓诘淖詈貌檎視r(shí)間復(fù)雜度為O(1),并且如果用到set中那么查找時(shí)間復(fù)雜度的一直是O(1),因?yàn)閟et中是不允許有元素重復(fù)的。而紅黑樹(shù)的查找時(shí)間復(fù)雜度為O(lgn)
68 hash_map與map的區(qū)別?什么時(shí)候用hash_map,什么時(shí)候用map?
- 構(gòu)造函數(shù):hash_map需要hash function和等于函數(shù),而map需要比較函數(shù)(大于或小于)。
- 存儲(chǔ)結(jié)構(gòu):hash_map以hashtable為底層,而map以RB-TREE為底層。
- 總的說(shuō)來(lái),hash_map查找速度比map快,而且查找速度基本和數(shù)據(jù)量大小無(wú)關(guān),屬于常數(shù)級(jí)別。而map的查找速度是logn級(jí)別。但不一定常數(shù)就比log小,而且hash_map還有hash function耗時(shí)。
- 如果考慮效率,特別當(dāng)元素達(dá)到一定數(shù)量級(jí)時(shí),用hash_map。
- 考慮內(nèi)存,或者元素?cái)?shù)量較少時(shí),用map。
69 迭代器失效的問(wèn)題
插入操作:
- 對(duì)于vector和string,如果容器內(nèi)存被重新分配,iterators,pointers,references失效;如果沒(méi)有重新分配,那么插入點(diǎn)之前的iterator有效,插入點(diǎn)之后的iterator失效;
- 對(duì)于deque,如果插入點(diǎn)位于除front和back的其它位置,iterators,pointers,references失效;當(dāng)我們插入元素到front和back時(shí),deque的迭代器失效,但reference和pointers有效;
- 對(duì)于list和forward_list,所有的iterator,pointer和refercnce有效。刪除操作:
- 對(duì)于vector和string,刪除點(diǎn)之前的iterators,pointers,references有效;off-the-end迭代器總是失效的;
- 對(duì)于deque,如果刪除點(diǎn)位于除front和back的其它位置,iterators,pointers,references失效;當(dāng)我們插入元素到front和back時(shí),off-the-end失效,其他的iterators,pointers,references有效;
- 對(duì)于list和forward_list,所有的iterator,pointer和refercnce有效。
- 對(duì)于關(guān)聯(lián)容器map來(lái)說(shuō),如果某一個(gè)元素已經(jīng)被刪除,那么其對(duì)應(yīng)的迭代器就失效了,不應(yīng)該再被使用,否則會(huì)導(dǎo)致程序無(wú)定義的行為。
70 STL線程不安全的情況
- 在對(duì)同一個(gè)容器進(jìn)行多線程的讀寫(xiě)、寫(xiě)操作時(shí);
- 在每次調(diào)用容器的成員函數(shù)期間都要鎖定該容器;
- 在每個(gè)容器返回的迭代器(例如通過(guò)調(diào)用begin或end)的生存期之內(nèi)都要鎖定該容器;
- 在每個(gè)在容器上調(diào)用的算法執(zhí)行期間鎖定該容器。