1. 變量的聲明和定義有什么區別
變量的定義為變量分配地址和存儲空間, 變量的聲明不分配地址。一個變量可以在多個地方聲明, 但是只在一個地方定義。加入extern 修飾的是變量的聲明,說明此變量將在文件以外或在文件后面部分定義。
說明:很多時候一個變量,只是聲明不分配內存空間,直到具體使用時才初始化,分配內存空間, 如外部變量。
intmain()
{
externintA;
//這是個聲明而不是定義,聲明A是一個已經定義了的外部變量
//注意:聲明外部變量時可以把變量類型去掉如:extern A;
dosth();//執行函數
}
intA;//是定義,定義了A為整型的外部變量
2. 簡述#ifdef、#else、#endif和#ifndef的作用
利用#ifdef、#endif將某程序功能模塊包括進去,以向特定用戶提供該功能。在不需要時用戶可輕易將其屏蔽。
#ifdefMATH
#include"math.c"
#endif
在子程序前加上標記,以便于追蹤和調試。
#ifdefDEBUG
printf("Indebugging......!");
#endif
應對硬件的限制。由于一些具體應用環境的硬件不一樣,限于條件,本地缺乏這種設備,只能繞過硬件,直接寫出預期結果。
「注意」:雖然不用條件編譯命令而直接用if語句也能達到要求,但那樣做目標程序長(因為所有語句都編譯),運行時間長(因為在程序運行時間對if語句進行測試)。而采用條件編譯,可以減少被編譯的語句,從而減少目標程序的長度,減少運行時間。
3. 寫出int、bool、float、指針變量與“零值”比較的if語句
//int與零值比較
if(n==0)
if(n!=0)
//bool與零值比較
if(flag)//表示flag為真
if(!flag)//表示flag為假
//float與零值比較
constfloatEPSINON=0.00001;
if((x >=- EPSINON)&&(x <= EPSINON)?//其中EPSINON是允許的誤差(即精度)。
//指針變量與零值比較?
if(p==NULL)
if(p!=NULL)
4. 結構體可以直接賦值嗎?
聲明時可以直接初始化,同一結構體的不同對象之間也可以直接賦值,但是當結構體中含有指針“成員”時一定要小心。
「注意」:當有多個指針指向同一段內存時,某個指針釋放這段內存可能會導致其他指針的非法操作。因此在釋放前一定要確保其他指針不再使用這段內存空間。
5. sizeof和strlen的區別
- sizeof是一個操作符,strlen是庫函數。
- sizeof的參數可以是數據的類型,也可以是變量,而strlen只能以結尾為‘’的字符串作參數。
- 編譯器在編譯時就計算出了sizeof的結果,而strlen函數必須在運行時才能計算出來。并且sizeof計算的是數據類型占內存的大小,而strlen計算的是字符串實際的長度。
- 數組做sizeof的參數不退化,傳遞給strlen就退化為指針了
6. C語言的關鍵字static和C++的關鍵字static有什么區別?
在 C 中 static 用來修飾局部靜態變量和外部靜態變量、函數。而 C++中除了上述功能外,還用來定義類的成員變量和函數。即靜態成員和靜態成員函數。
「注意」:編程時 static 的記憶性,和全局性的特點可以讓在不同時期調用的函數進行通信,傳遞信息,而 C++的靜態成員則可以在多個對象實例間進行通信,傳遞信息。
7. C語言的malloc和C++中的new有什么區別?
- new 、delete 是操作符,可以重載,只能在C++ 中使用。
- malloc、free 是函數,可以覆蓋,C、C++ 中都可以使用。
- new 可以調用對象的構造函數,對應的delete 調用相應的析構函數。
- malloc 僅僅分配內存,free 僅僅回收內存,并不執行構造和析構函數
- new 、delete 返回的是某種數據類型指針,malloc、free 返回的是void 指針。
「注意」:malloc 申請的內存空間要用free 釋放,而new 申請的內存空間要用delete 釋放,不要混用。
8. 寫一個“標準”宏MIN
#define min(a,b)((a)<=(b)?(a):(b))
9. ++i和i++的區別
++i先自增1,再返回,i++先返回i,再自增1
10. volatile有什么作用?
- 狀態寄存器一類的并行設備硬件寄存器。
- 一個中斷服務子程序會訪問到的非自動變量。
- 多線程間被幾個任務共享的變量。
「注意」:雖然volatile在嵌入式方面應用比較多,但是在PC軟件的多線程中,volatile修飾的臨界變量也是非常實用的。
11. 一個參數可以既是const又是volatile嗎?
可以,用const和volatile同時修飾變量,表示這個變量在程序內部是只讀的,不能改變的,只在程序外部條件變化下改變,并且編譯器不會優化這個變量。每次使用這個變量時,都要小心地去內存讀取這個變量的值,而不是去寄存器讀取它的備份。
注意:在此一定要注意const的意思,const只是不允許程序中的代碼改變某一變量,其在編譯期發揮作用,它并沒有實際地禁止某段內存的讀寫特性。
12. a和&a有什么區別?
&a:其含義就是“變量a的地址”。
*a:用在不同的地方,含義也不一樣。
- 在聲明語句中,*a只說明a是一個指針變量,如int *a;
- 在其他語句中,*a前面沒有操作數且a是一個指針時,*a代表指針a指向的地址內存放的數據,如b=*a;
- *a前面有操作數且a是一個普通變量時,a代表乘以a,如c=ba。
13. 用C編寫一個死循環程序
while(1)
{}
「注意」:很多種途徑都可實現同一種功能,但是不同的方法時間和空間占用度不同,特別是對于嵌入 式軟件,處理器速度比較慢,存儲空間較小,所以時間和空間優勢是選擇各種方法的首要考慮條件。
14. 結構體內存對齊問題
請寫出以下代碼的輸出結果:
#include
structS1
{
inti:8;
charj:4;
inta:4;
doubleb;
};
structS2
{
inti:8;
charj:4;
doubleb;
inta:4;
};
structS3
{
inti;
charj;
doubleb;
inta;
};
intmain()
{
printf("%d
",sizeof(S1));//輸出8
printf("%d
",sizeof(S1);//輸出12
printf("%d
",sizeof(Test3));//輸出8
return0;
}
sizeof(S1)=16
sizeof(S2)=24
sizeof(S3)=32
「說明」:結構體作為一種復合數據類型,其構成元素既可以是基本數據類型的變量,也可以是一些復合型類型數據。對此,編譯器會自動進行成員變量的對齊以提高運算效率。默認情況下,按自然對齊條件分配空間。各個成員按照它們被聲明的順序在內存中順序存儲,第一個成員的地址和整個結構的地址相同,向結構體成員中size最大的成員對齊。
許多實際的計算機系統對基本類型數據在內存中存放的位置有限制,它們會要求這些數據的首地址的值是某個數k(通常它為4或8)的倍數,而這個k則被稱為該數據類型的對齊模數。
15. 全局變量和局部變量有什么區別?怎么實現的?操作系統和編譯器是怎么知道的?
- 全局變量是整個程序都可訪問的變量,誰都可以訪問,生存期在整個程序從運行到結束(在程序結束時所占內存釋放);
- 而局部變量存在于模塊(子程序,函數)中,只有所在模塊可以訪問,其他模塊不可直接訪問,模塊結束(函數調用完畢),局部變量消失,所占據的內存釋放。
- 操作系統和編譯器,可能是通過內存分配的位置來知道的,全局變量分配在全局數據段并且在程序開始運行的時候被加載.局部變量則分配在堆棧里面。
16. 簡述C、C++程序編譯的內存分配情況
- 從靜態存儲區域分配:
內存在程序編譯時就已經分配好,這塊內存在程序的整個運行期間都存在。速度快、不容易出錯, 因為有系統會善后。例如全局變量,static 變量,常量字符串等。
- 在棧上分配:
在執行函數時,函數內局部變量的存儲單元都在棧上創建,函數執行結束時這些存儲單元自動被釋 放。棧內存分配運算內置于處理器的指令集中,效率很高,但是分配的內存容量有限。大小為2M。
- 從堆上分配:
即動態內存分配。程序在運行的時候用 malloc 或new 申請任意大小的內存,程序員自己負責在何 時用free 或delete 釋放內存。動態內存的生存期由程序員決定,使用非常靈活。如果在堆上分配了空間,就有責任回收它,否則運行的程序會出現內存泄漏,另外頻繁地分配和釋放不同大小的堆空間將會產生 堆內碎塊。
一個C、C++程序編譯時內存分為5 大存儲區:堆區、棧區、全局區、文字常量區、程序代碼區。
17. 簡述strcpy、sprintf與memcpy的區別
- 操作對象不同,strcpy 的兩個操作對象均為字符串,sprintf 的操作源對象可以是多種數據類型, 目的操作對象是字符串,memcpy 的兩個對象就是兩個任意可操作的內存地址,并不限于何種數據類型。
- 執行效率不同,memcpy 最高,strcpy 次之,sprintf 的效率最低。
- 實現功能不同,strcpy 主要實現字符串變量間的拷貝,sprintf 主要實現其他數據類型格式到字 符串的轉化,memcpy 主要是內存塊間的拷貝。
「注意」:strcpy、sprintf 與memcpy 都可以實現拷貝的功能,但是針對的對象不同,根據實際需求,來 選擇合適的函數實現拷貝功能。
18. 解析((void ()( ) )0)( )的含義
- void (*0)( ) :是一個返回值為void,參數為空的函數指針0。
- (void (*)( ))0:把0轉變成一個返回值為void,參數為空的函數指針。
- (void ()( ))0:在上句的基礎上加*表示整個是一個返回值為void,無參數,并且起始地址為0的函數的名字。
- ((void ()( ))0)( ):這就是上句的函數名所對應的函數的調用。
19. C語言的指針和引用和C++的有什么區別?
- 指針有自己的一塊空間,而引用只是一個別名;
- 使用sizeof看一個指針的大小是4,而引用則是被引用對象的大小;
- 作為參數傳遞時,指針需要被解引用才可以對對象進行操作,而直接對引 用的修改都會改變引用所指向的對象;
- 可以有const指針,但是沒有const引用;
- 指針在使用中可以指向其它對象,但是引用只能是一個對象的引用,不能 被改變;
- 指針可以有多級指針(**p),而引用止于一級;
- 指針和引用使用++運算符的意義不一樣;
- 如果返回動態內存分配的對象或者內存,必須使用指針,引用可能引起內存泄露。
20. typedef和define有什么區別?
- 用法不同:typedef 用來定義一種數據類型的別名,增強程序的可讀性。define 主要用來定義 常量,以及書寫復雜使用頻繁的宏。
- 執行時間不同:typedef 是編譯過程的一部分,有類型檢查的功能。define 是宏定義,是預編譯的部分,其發生在編譯之前,只是簡單的進行字符串的替換,不進行類型的檢查。
- 作用域不同:typedef 有作用域限定。define 不受作用域約束,只要是在define 聲明后的引用 都是正確的。
- 對指針的操作不同:typedef 和define 定義的指針時有很大的區別。
「注意」:typedef 定義是語句,因為句尾要加上分號。而define 不是語句,千萬不能在句尾加分號。
21. 指針常量與常量指針的區別
指針常量是指定義了一個指針,這個指針的值只能在定義時初始化,其他地方不能改變。常量指針 是指定義了一個指針,這個指針指向一個只讀的對象,不能通過常量指針來改變這個對象的值。指針常量強調的是指針的不可改變性,而常量指針強調的是指針對其所指對象的不可改變性。
「注意」:無論是指針常量還是常量指針,其最大的用途就是作為函數的形式參數,保證實參在被調用 函數中的不可改變特性。
22. 簡述隊列和棧的異同
隊列和棧都是線性存儲結構,但是兩者的插入和刪除數據的操作不同,隊列是“先進先出”,棧是 “后進先出”。
「注意」:區別棧區和堆區。堆區的存取是“順序隨意”,而棧區是“后進先出”。棧由編譯器自動分 配釋放 ,存放函數的參數值,局部變量的值等。其操作方式類似于數據結構中的棧。堆一般由程序員 分配釋放, 若程序員不釋放,程序結束時可能由OS 回收。分配方式類似于鏈表。它與本題中的堆和棧是兩回事。堆棧只是一種數據結構,而堆區和棧區是程序的不同內存存儲區域。
23. 設置地址為0x67a9的整型變量的值為0xaa66
int*ptr;
ptr=(int*)0x67a9;
*ptr=0xaa66;
「注意」:這道題就是強制類型轉換的典型例子,無論在什么平臺地址長度和整型數據的長度是一樣的, 即一個整型數據可以強制轉換成地址指針類型,只要有意義即可。
24. 編碼實現字符串轉化為數字
編碼實現函數atoi(),設計一個程序,把一個字符串轉化為一個整型數值。例如數字:“5486321 ”, 轉化成字符:5486321。
intmyAtoi(constchar*str)
{
intnum=0;//保存轉換后的數值
intisNegative=0;//記錄字符串中是否有負號
intn=0;
char*p=str;
if(p==NULL)//判斷指針的合法性
{
return-1;
}
while(*p++!='')//計算數字符串度
{
n++;
}
p=str;
if(p[0]=='-')//判斷數組是否有負號
{
isNegative=1;
}
chartemp='0';
for(inti=0;iif(temp>'9'||temp'0')//濾除非數字字符
{
continue;
}
if(num!=0||temp!='0')//濾除字符串開始的0字符
{
temp-=0x30;//將數字字符轉換為數值
num+=temp*int(pow(10,n-1-i));
}
}
if(isNegative)//如果字符串中有負號,將數值取反
{
return(0-num);
}
else
{
returnnum;//返回轉換后的數值
}
}
25. C語言的結構體和C++的有什么區別?
- C語言的結構體是不能有函數成員的,而C++的類可以有。
- C語言的結構體中數據成員是沒有private、public和protected訪問限定的。而C++的類的成員有這些訪問限定。
- C語言的結構體是沒有繼承關系的,而C++的類卻有豐富的繼承關系。
「注意」:雖然C的結構體和C++的類有很大的相似度,但是類是實現面向對象的基礎。而結構體只可以簡單地理解為類的前身。
26. 簡述指針常量與常量指針的區別
- 指針常量是指定義了一個指針,這個指針的值只能在定義時初始化,其他地方不能改變。常量指針是指定義了一個指針,這個指針指向一個只讀的對象,不能通過常量指針來改變這個對象的值。
- 指針常量強調的是指針的不可改變性,而常量指針強調的是指針對其所指對象的不可改變性。
「注意」:無論是指針常量還是常量指針,其最大的用途就是作為函數的形式參數,保證實參在被調用函數中的不可改變特性。
27. 如何避免“野指針”?
- 指針變量聲明時沒有被初始化。解決辦法:指針聲明時初始化,可以是具體的地址值,也可讓它指向NULL。
- 指針p被free或者delete之后,沒有置為NULL。解決辦法:指針指向的內存空間被釋放后指針應該指向NULL。
- 指針操作超越了變量的作用范圍。解決辦法:在變量的作用域結束前釋放掉變量的地址空間并且讓指針指向NULL。
28. 句柄和指針的區別和聯系是什么?
句柄和指針其實是兩個截然不同的概念。Windows系統用句柄標記系統資源,隱藏系統的信息。你只要知道有這個東西,然后去調用就行了,它是個32it的uint。指針則標記某個物理內存地址,兩者是不同的概念。
29. new/delete與malloc/free的區別是什么?
- new能自動計算需要分配的內存空間,而malloc需要手工計算字節數。
int*p=newint[2];
int*q=(int*)malloc(2*sizeof(int));
- new與delete直接帶具體類型的指針,malloc和free返回void類型的指針。
- new類型是安全的,而malloc不是。例如int *p = new float[2];就會報錯;而int p = malloc(2sizeof(int))編譯時編譯器就無法指出錯誤來。
- new一般分為兩步:new操作和構造。new操作對應與malloc,但new操作可以重載,可以自定義內存分配策略,不做內存分配,甚至分配到非內存設備上,而malloc不行。
- new調用構造函數,malloc不能;delete調用析構函數,而free不能。
- malloc/free需要庫文件stdlib.h的支持,new/delete則不需要!
「注意」:delete和free被調用后,內存不會立即回收,指針也不會指向空,delete或free僅僅是告訴操作系統,這一塊內存被釋放了,可以用作其他用途。但是由于沒有重新對這塊內存進行寫操作,所以內存中的變量數值并沒有發生變化,出現野指針的情況。因此,釋放完內存后,應該講該指針指向NULL。
30. 說一說extern“C”
extern "C"的主要作用就是為了能夠正確實現C++代碼調用其他C語言代碼。加上extern "C"后,會指示編譯器這部分代碼按C語言(而不是C++)的方式進行編譯。由于C++支持函數重載,因此編譯器編譯函數的過程中會將函數的參數類型也加到編譯后的代碼中,而不僅僅是函數名;而C語言并不支持函數重載,因此編譯C語言代碼的函數時不會帶上函數的參數類型,一般只包括函數名。
這個功能十分有用處,因為在C++出現以前,很多代碼都是C語言寫的,而且很底層的庫也是C語言寫的,為了更好的支持原來的C代碼和已經寫好的C語言庫,需要在C++中盡可能的支持C,而extern "C"就是其中的一個策略。
- C++代碼調用C語言代碼
- 在C++的頭文件中使用
- 在多個人協同開發時,可能有的人比較擅長C語言,而有的人擅長C++,這樣的情況下也會有用到
31. C++中struct和class的區別
在C++中,class和struct做類型定義是只有兩點區別:
- 默認繼承權限不同,class繼承默認是private繼承,而struct默認是public繼承
- class還可用于定義模板參數,像typename,但是關鍵字struct不能同于定義模板參數 C++保留struct關鍵字,原因
- 保證與C語言的向下兼容性,C++必須提供一個struct
- C++中的struct定義必須百分百地保證與C語言中的struct的向下兼容性,把C++中的最基本的對象單元規定為class而不是struct,就是為了避免各種兼容性要求的限制
- 對struct定義的擴展使C語言的代碼能夠更容易的被移植到C++中
32. C++類內可以定義引用數據成員嗎?
可以,必須通過成員函數初始化列表初始化。
33. C++中類成員的訪問權限
C++通過 public、protected、private 三個關鍵字來控制成員變量和成員函數的訪問權限,它們分別表示公有的、受保護的、私有的,被稱為成員訪問限定符。在類的內部(定義類的代碼內部),無論成員被聲明為 public、protected 還是 private,都是可以互相訪問的,沒有訪問權限的限制。在類的外部(定義類的代碼之外),只能通過對象訪問成員,并且通過對象只能訪問 public 屬性的成員,不能訪問 private、protected 屬性的成員
34. 什么是右值引用?跟左值又有什么區別?
左值和右值的概念:
- 左值:能取地址,或者具名對象,表達式結束后依然存在的持久對象;
- 右值:不能取地址,匿名對象,表達式結束后就不再存在的臨時對象;區別:
- 左值能賦值,右值不能;
- 左值可變,右值不能(僅對基礎類型適用,用戶自定義類型右值引用可以通過成員函數改變);
35. 面向對象的三大特征
- 封裝性:將客觀事物抽象成類,每個類對自身的數據和方法實行 protection (private , protected , public )。
- 繼承性:廣義的繼承有三種實現形式:實現繼承(使用基類的屬性和方法而無需額外編碼的能力)、可 視繼承(子窗體使用父窗體的外觀和實現代碼)、接口繼承(僅使用屬性和方法,實現滯后到子類實現)。
- 多態性:是將父類對象設置成為和一個或更多它的子對象相等的技術。用子類對象給父類對象賦值 之后,父類對象就可以根據當前賦值給它的子對象的特性以不同的方式運作。
36. 說一說c++中四種cast轉換
C++中四種類型轉換是:static_cast, dynamic_cast, const_cast, reinterpret_cast
1、const_cast
- 用于將const變量轉為非const
2、static_cast
- 用于各種隱式轉換,比如非const轉const,void*轉指針等, static_cast能用于多態向上轉化,如果向下轉能成功但是不安全,結果未知;
3、dynamic_cast
用于動態類型轉換。只能用于含有虛函數的類,用于類層次間的向上和向下轉化。只能轉指針或引用。向下轉化時,如果是非法的***對于指針返回NULL,對于引用拋異常***。要深入了解內部轉換的原理。
- 向上轉換:指的是子類向基類的轉換
- 向下轉換:指的是基類向子類的轉換
它通過判斷在執行到該語句的時候變量的運行時類型和要轉換的類型是否相同來判斷是否能夠進行向下轉換。
4、reinterpret_cast
- 幾乎什么都可以轉,比如將int轉指針,可能會出問題,盡量少用;
5、為什么不使用C的強制轉換?
- C的強制轉換表面上看起來功能強大什么都能轉,但是轉化不夠明確,不能進行錯誤檢查,容易出錯。
37. C++的空類有哪些成員函數?
- 缺省構造函數。
- 缺省拷貝構造函數。
- 省析構函數。
- 賦值運算符。
- 取址運算符。
- 取址運算符 const 。
「注意」:有些書上只是簡單的介紹了前四個函數。沒有提及后面這兩個函數。但后面這兩個函數也是 空類的默認函數。另外需要注意的是,只有當實際使用這些函數的時候,編譯器才會去定義它們。
38. 對c++中的smart pointer四個智能指針:shared_ptr,unique_ptr,weak_ptr,auto_ptr的理解
C++里面的四個智能指針: auto_ptr, shared_ptr, weak_ptr, unique_ptr 其中后三個是c++11支持,并且第一個已經被11棄用。
智能指針的作用是管理一個指針,因為存在以下這種情況:申請的空間在函數結束時忘記釋放,造成內存泄漏。使用智能指針可以很大程度上的避免這個問題,因為智能指針就是一個類,當超出了類的作用域是,類會自動調用析構函數,析構函數會自動釋放資源。所以智能指針的作用原理就是在函數結束時自動釋放內存空間,不需要手動釋放內存空間。
- auto_ptr(c++98的方案,cpp11已經拋棄)
采用所有權模式。
auto_ptrp1(newstring("Ireignedlonelyasacloud.”));
auto_ptrp2;
p2=p1;//auto_ptr不會報錯.
此時不會報錯,p2剝奪了p1的所有權,但是當程序運行時訪問p1將會報錯。所以auto_ptr的缺點是:存在潛在的內存崩潰問題!
- unique_ptr(替換auto_ptr)
unique_ptr實現獨占式擁有或嚴格擁有概念,保證同一時間內只有一個智能指針可以指向該對象。它對于避免資源泄露(例如“以new創建對象后因為發生異常而忘記調用delete”)特別有用。
采用所有權模式。
unique_ptrp3(newstring("auto"));//#4
unique_ptr p4;//#5
p4 = p3;//此時會報錯!!
編譯器認為p4=p3非法,避免了p3不再指向有效數據的問題。因此,unique_ptr比auto_ptr更安全。
另外unique_ptr還有更聰明的地方:當程序試圖將一個 unique_ptr 賦值給另一個時,如果源 unique_ptr 是個臨時右值,編譯器允許這么做;如果源 unique_ptr 將存在一段時間,編譯器將禁止這么做,比如:
unique_ptrpu1(newstring("helloworld"));
unique_ptrpu2;
pu2=pu1;//#1notallowed
unique_ptrpu3;
pu3=unique_ptr(newstring("You"));//#2allowed
其中#1留下懸掛的unique_ptr(pu1),這可能導致危害。而#2不會留下懸掛的unique_ptr,因為它調用 unique_ptr 的構造函數,該構造函數創建的臨時對象在其所有權讓給 pu3 后就會被銷毀。這種隨情況而已的行為表明,unique_ptr 優于允許兩種賦值的auto_ptr 。
「注意」:如果確實想執行類似與#1的操作,要安全的重用這種指針,可給它賦新值。C++有一個標準庫函數std::move(),讓你能夠將一個unique_ptr賦給另一個。例如:
unique_ptrps1,ps2;
ps1=demo("hello");
ps2=move(ps1);
ps1=demo("alexia");
cout<*ps2?<*ps1?<
- shared_ptr
shared_ptr實現共享式擁有概念。多個智能指針可以指向相同對象,該對象和其相關資源會在“最后一個引用被銷毀”時候釋放。從名字share就可以看出了資源可以被多個指針共享,它使用計數機制來表明資源被幾個指針共享。可以通過成員函數use_count()來查看資源的所有者個數。除了可以通過new來構造,還可以通過傳入auto_ptr, unique_ptr,weak_ptr來構造。當我們調用release()時,當前指針會釋放資源所有權,計數減一。當計數等于0時,資源會被釋放。
shared_ptr 是為了解決 auto_ptr 在對象所有權上的局限性(auto_ptr 是獨占的), 在使用引用計數的機制上提供了可以共享所有權的智能指針。
成員函數:
use_count 返回引用計數的個數
unique 返回是否是獨占所有權( use_count 為 1)
swap 交換兩個 shared_ptr 對象(即交換所擁有的對象)
reset 放棄內部對象的所有權或擁有對象的變更, 會引起原有對象的引用計數的減少
get 返回內部對象(指針), 由于已經重載了()方法, 因此和直接使用對象是一樣的.如 shared_ptrsp(new int(1)); sp 與 sp.get()是等價的
- weak_ptr
weak_ptr 是一種不控制對象生命周期的智能指針, 它指向一個 shared_ptr 管理的對象. 進行該對象的內存管理的是那個強引用的 shared_ptr. weak_ptr只是提供了對管理對象的一個訪問手段。weak_ptr 設計的目的是為配合 shared_ptr 而引入的一種智能指針來協助 shared_ptr 工作, 它只可以從一個 shared_ptr 或另一個 weak_ptr 對象構造, 它的構造和析構不會引起引用記數的增加或減少。weak_ptr是用來解決shared_ptr相互引用時的死鎖問題,如果說兩個shared_ptr相互引用,那么這兩個指針的引用計數永遠不可能下降為0,資源永遠不會釋放。它是對對象的一種弱引用,不會增加對象的引用計數,和shared_ptr之間可以相互轉化,shared_ptr可以直接賦值給它,它可以通過調用lock函數來獲得shared_ptr。
classB;
classA
{
public:
shared_ptrpb_;
~A()
{
cout<<"Adelete
";
}
};
classB
{
public:
shared_ptrpa_;
~B()
{
cout<<"Bdelete
";
}
};
voidfun()
{
shared_ptrpb(newB());
shared_ptrpa(newA());
pb->pa_=pa;
pa->pb_=pb;
cout<main()
{
fun();
return0;
}
可以看到fun函數中pa ,pb之間互相引用,兩個資源的引用計數為2,當要跳出函數時,智能指針pa,pb析構時兩個資源引用計數會減一,但是兩者引用計數還是為1,導致跳出函數時資源沒有被釋放(A B的析構函數沒有被調用),如果把其中一個改為weak_ptr就可以了,我們把類A里面的shared_ptr pb_; 改為weak_ptr pb_; 運行結果如下,這樣的話,資源B的引用開始就只有1,當pb析構時,B的計數變為0,B得到釋放,B釋放的同時也會使A的計數減一,同時pa析構時使A的計數減一,那么A的計數為0,A得到釋放。
「注意」:不能通過weak_ptr直接訪問對象的方法,比如B對象中有一個方法print(),我們不能這樣訪問,pa->pb_->print(); 英文pb_是一個weak_ptr,應該先把它轉化為shared_ptr,如:shared_ptr p = pa->pb_.lock(); p->print();
39. 說說強制類型轉換運算符
「static_cast」
- 用于非多態類型的轉換
- 不執行運行時類型檢查(轉換安全性不如 dynamic_cast)
- 通常用于轉換數值數據類型(如 float -> int)
- 可以在整個類層次結構中移動指針,子類轉化為父類安全(向上轉換),父類轉化為子類不安全(因為子類可能有不在父類的字段或方法)
「dynamic_cast」
- 用于多態類型的轉換
- 執行行運行時類型檢查
- 只適用于指針或引用
- 對不明確的指針的轉換將失敗(返回 nullptr),但不引發異常
- 可以在整個類層次結構中移動指針,包括向上轉換、向下轉換
「const_cast」
- 用于刪除 const、volatile 和 __unaligned 特性(如將 const int 類型轉換為 int 類型 ) reinterpret_cast
- 用于位的簡單重新解釋
- 濫用 reinterpret_cast 運算符可能很容易帶來風險。除非所需轉換本身是低級別的,否則應- 使用其他強制轉換運算符之一。
- 允許將任何指針轉換為任何其他指針類型(如 char* 到 int* 或 One_class* 到 Unrelated_class* 之類的轉換,但其本身并不安全)
- 也允許將任何整數類型轉換為任何指針類型以及反向轉換。
- reinterpret_cast 運算符不能丟掉 const、volatile 或 __unaligned 特性。
- reinterpret_cast 的一個實際用途是在哈希函數中,即,通過讓兩個不同的值幾乎不以相同的索引結尾的方式將值映射到索引。
「bad_cast」
- 由于強制轉換為引用類型失敗,dynamic_cast 運算符引發 bad_cast 異常。
bad_cast 使用
try{
Circle&ref_circle=dynamic_cast(ref_shape);
}
catch(bad_castb){
cout<"Caught:"<
40. 談談你對拷貝構造函數和賦值運算符的認識
拷貝構造函數和賦值運算符重載有以下兩個不同之處:
- 拷貝構造函數生成新的類對象,而賦值運算符不能。
- 由于拷貝構造函數是直接構造一個新的類對象,所以在初始化這個對象之前不用檢驗源對象 是否和新建對象相同。而賦值運算符則需要這個操作,另外賦值運算中如果原來的對象中有內存分配要先把內存釋放掉。
「注意」:當有類中有指針類型的成員變量時,一定要重寫拷貝構造函數和賦值運算符,不要使用默認 的。
41. 在C++中,使用malloc申請的內存能否通過delete釋放?使用new申請的內存能否用free?
不能,malloc /free主要為了兼容C,new和delete 完全可以取代malloc /free的。malloc /free的操作對象都是必須明確大小的。而且不能用在動態類上。new 和delete會自動進行類型檢查和大小,malloc/free不能執行構造函數與析構函數,所以動態對象它是不行的。當然從理論上說使用malloc申請的內存是可以通過delete釋放的。不過一般不這樣寫的。而且也不能保證每個C++的運行時都能正常。
42. 用C++設計一個不能被繼承的類
templateclassA
{
friendT;
private:
A(){}
~A(){}
};
classB:virtualpublicA
{
public:
B(){}
~B(){}
};
classC:virtualpublicB
{
public:
C(){}
~C(){}
};
voidmain(void)
{
Bb;
//Cc;
return;
}
「注意」:構造函數是繼承實現的關鍵,每次子類對象構造時,首先調用的是父類的構造函數,然后才 是自己的。
43. C++自己實現一個String類
#include
#include
usingnamespacestd;
classString{
public:
//默認構造函數
String(constchar*str=nullptr);
//拷貝構造函數
String(constString&str);
//析構函數
~String();
//字符串賦值函數
String&operator=(constString&str);
private:
char*m_data;
intm_size;
};
//構造函數
String::String(constchar*str)
{
if(str == nullptr)//加分點:對m_data加NULL 判斷
{
m_data = new char[1];//得分點:對空字符串自動申請存放結束標志''的
m_data[0]='';
m_size=0;
}
else
{
m_size=strlen(str);
m_data=newchar[m_size+1];
strcpy(m_data,str);
}
}
//拷貝構造函數
String::String(const String &str)//得分點:輸入參數為const型
{
m_size=str.m_size;
m_data = new char[m_size + 1];//加分點:對m_data加NULL 判斷
strcpy(m_data,str.m_data);
}
//析構函數
String::~String()
{
delete[]m_data;
}
//字符串賦值函數
String& String::operator=(const String &str)//得分點:輸入參數為const
{
if(this ==&str)//得分點:檢查自賦值
return*this;
delete[] m_data;//得分點:釋放原有的內存資源
m_size=strlen(str.m_data);
m_data = new char[m_size + 1];//加分點:對m_data加NULL 判斷
strcpy(m_data,str.m_data);
return*this;//得分點:返回本對象的引用
}
44. 訪問基類的私有虛函數
寫出以下程序的輸出結果:
#include
classA
{
virtualvoidg()
{
cout<"A::g"<f()
{
cout<"A::f"<g()
{
cout<"B::g"<h()
{
cout<"B::h"<main()
{
Bb;
FunpFun;
for(inti=0;i3;?i++)?
???{?
??????pFun?=?(?Fun?)*(?(?int*?)?*?(?int*?)(?&b?)?+?i?);?
??????pFun();?
???}?
}?
輸出結果:
B::g
A::f
B::h
「注意」:考察了面試者對虛函數的理解程度。一個對虛函數不了解的人很難正確的做出本題。在學習面向對象的多態性時一定要深刻理解虛函數表的工作原理。
45. 對虛函數和多態的理解
多態的實現主要分為靜態多態和動態多態,靜態多態主要是重載,在編譯的時候就已經確定;動態多態是用虛函數機制實現的,在運行期間動態綁定。舉個例子:一個父類類型的指針指向一個子類對象時候,使用父類的指針去調用子類中重寫了的父類中的虛函數的時候,會調用子類重寫過后的函數,在父類中聲明為加了virtual關鍵字的函數,在子類中重寫時候不需要加virtual也是虛函數。
虛函數的實現:在有虛函數的類中,類的最開始部分是一個虛函數表的指針,這個指針指向一個虛函數表,表中放了虛函數的地址,實際的虛函數在代碼段(.text)中。當子類繼承了父類的時候也會繼承其虛函數表,當子類重寫父類中虛函數時候,會將其繼承到的虛函數表中的地址替換為重新寫的函數地址。使用了虛函數,會增加訪問內存開銷,降低效率。
46. 簡述類成員函數的重寫、重載和隱藏的區別
(1)重寫和重載主要有以下幾點不同。
- 范圍的區別:被重寫的和重寫的函數在兩個類中,而重載和被重載的函數在同一個類中。
- 參數的區別:被重寫函數和重寫函數的參數列表一定相同,而被重載函數和重載函數的參數列表一 定不同。
- virtual 的區別:重寫的基類中被重寫的函數必須要有virtual 修飾,而重載函數和被重載函數可以被 virtual 修飾,也可以沒有。
(2)隱藏和重寫、重載有以下幾點不同。
- 與重載的范圍不同:和重寫一樣,隱藏函數和被隱藏函數不在同一個類中。
- 參數的區別:隱藏函數和被隱藏的函數的參數列表可以相同,也可不同,但是函數名肯定要相同。當參數不相同時,無論基類中的參數是否被virtual 修飾,基類的函數都是被隱藏,而不是被重寫。
「注意」:雖然重載和覆蓋都是實現多態的基礎,但是兩者實現的技術完全不相同,達到的目的也是完 全不同的,覆蓋是動態態綁定的多態,而重載是靜態綁定的多態。
47. 鏈表和數組有什么區別
- 存儲形式:數組是一塊連續的空間,聲明時就要確定長度。鏈表是一塊可不連續的動態空間, 長度可變,每個結點要保存相鄰結點指針。
- 數據查找:數組的線性查找速度快,查找操作直接使用偏移地址。鏈表需要按順序檢索結點, 效率低。
- 數據插入或刪除:鏈表可以快速插入和刪除結點,而數組則可能需要大量數據移動。
- 越界問題:鏈表不存在越界問題,數組有越界問題。
「注意」:在選擇數組或鏈表數據結構時,一定要根據實際需要進行選擇。數組便于查詢,鏈表便于插 入刪除。數組節省空間但是長度固定,鏈表雖然變長但是占了更多的存儲空間。
48. 用兩個棧實現一個隊列的功能
typedefstructnode
{
intdata;
node*next;
}node,*LinkStack;
//創建空棧:
LinkStackCreateNULLStack(LinkStack&S)
{
S=(LinkStack)malloc(sizeof(node));//申請新結點
if(NULL==S)
{
printf("Failtomallocanewnode.
");
returnNULL;
}
S->data=0;//初始化新結點
S->next=NULL;
returnS;
}
//棧的插入函數:
LinkStackPush(LinkStack&S,intdata)
{
if(NULL==S)//檢驗棧
{
printf("Therenonodeinstack!");
returnNULL;
}
LinkStackp=NULL;
p=(LinkStack)malloc(sizeof(node));//申請新結點
if(NULL==p)
{
printf("Failtomallocanewnode.
");
returnS;
}
if(NULL==S->next)
{
p->next=NULL;
}
else
{
p->next=S->next;
}
p->data=data;//初始化新結點
S->next=p;//插入新結點
returnS;
}
//出棧函數:
nodePop(LinkStack&S)
{
nodetemp;
temp.data=0;
temp.next=NULL;
if(NULL==S)//檢驗棧
{
printf("Therenonodeinstack!");
returntemp;
}
temp=*S;
if(S->next==NULL)
{
printf("ThestackisNULL,can'tpop!
");
returntemp;
}
LinkStackp=S->next;//節點出棧
S->next=S->next->next;
temp=*p;
free(p);
p=NULL;
returntemp;
}
//雙棧實現隊列的入隊函數:
LinkStackStackToQueuPush(LinkStack&S,intdata)
{
noden;
LinkStackS1=NULL;
CreateNULLStack(S1);//創建空棧
while(NULL!=S->next)//S出棧入S1
{
n=Pop(S);
Push(S1,n.data);
}
Push(S1,data);//新結點入棧
while(NULL!=S1->next)//S1出棧入S
{
n=Pop(S1);
Push(S,n.data);
}
returnS;
}
「注意」:用兩個棧能夠實現一個隊列的功能,那用兩個隊列能否實現一個隊列的功能呢?結果是否定 的,因為棧是先進后出,將兩個棧連在一起,就是先進先出。而隊列是現先進先出,無論多少個連在一 起都是先進先出,而無法實現先進后出。
49. 模板函數和模板類的特例化
「引入原因」
編寫單一的模板,它能適應多種類型的需求,使每種類型都具有相同的功能,但對于某種特定類型,如果要實現其特有的功能,單一模板就無法做到,這時就需要模板特例化
「定義」對單一模板提供的一個特殊實例,它將一個或多個模板參數綁定到特定的類型或值上
(1)模板函數特例化
必須為原函數模板的每個模板參數都提供實參,且使用關鍵字template后跟一個空尖括號對<>,表明將原模板的所有模板參數提供實參,舉例如下:
template//模板函數
intcompare(constT&v1,constT&v2)
{
if(v1>v2)return-1;
if(v2>v1)return1;
return0;
}
//模板特例化,滿足針對字符串特定的比較,要提供所有實參,這里只有一個T
template<>
intcompare(constchar*const&v1,constchar*const&v2)
{
returnstrcmp(p1,p2);
}
「本質」特例化的本質是實例化一個模板,而非重載它。特例化不影響參數匹配。參數匹配都以最佳匹配為原則。例如,此處如果是compare(3,5),則調用普通的模板,若為compare(“hi”,”haha”)則調用特例化版本(因為這個cosnt char*相對于T,更匹配實參類型),注意二者函數體的語句不一樣了,實現不同功能。
「注意」模板及其特例化版本應該聲明在同一個頭文件中,且所有同名模板的聲明應該放在前面,后面放特例化版本。
(2)類模板特例化
原理類似函數模板,不過在類中,我們可以對模板進行特例化,也可以對類進行部分特例化。對類進行特例化時,仍然用template<>表示是一個特例化版本,例如:
template<>
classhash
{
size_toperator()(sales_data&s);
//里面所有T都換成特例化類型版本sales_data
//按照最佳匹配原則,若T != sales_data,就用普通類模板,否則,就使用含有特定功能的特例化版本。
};
「類模板的部分特例化」
不必為所有模板參數提供實參,可以指定一部分而非所有模板參數,一個類模板的部分特例化本身仍是一個模板,使用它時還必須為其特例化版本中未指定的模板參數提供實參(特例化時類名一定要和原來的模板相同,只是參數類型不同,按最佳匹配原則,哪個最匹配,就用相應的模板)
「特例化類中的部分成員」
可以特例化類中的部分成員函數而不是整個類,舉個例子:
template
classFoo
{
voidBar();
voidBarst(Ta)();
};
template<>
voidFoo::Bar()
{
//進行int類型的特例化處理
cout<"我是int型特例化"<fs;
Foofi;//使用特例化
fs.Bar();//使用的是普通模板,即Foo::Bar()
fi.Bar();//特例化版本,執行Foo::Bar()
//Foo::Bar()和Foo::Bar()功能不同
50. 為什么析構函數一般寫成虛函數?
由于類的多態性,基類指針可以指向派生類的對象,如果刪除該基類的指針,就會調用該指針指向的派生類析構函數,而派生類的析構函數又自動調用基類的析構函數,這樣整個派生類的對象完全被釋放。如果析構函數不被聲明成虛函數,則編譯器實施靜態綁定,在刪除基類指針時,只會調用基類的析構函數而不調用派生類析構函數,這樣就會造成派生類對象析構不完全,造成內存泄漏。所以將析構函數聲明為虛函數是十分必要的。在實現多態時,當用基類操作派生類,在析構時防止只析構基類而不析構派生類的狀況發生,要將基類的析構函數聲明為虛函數。舉個例子:
#include
usingnamespacestd;
classParent{
public:
Parent(){
cout<"Parentconstructfunction"<Parent(){
cout<"Parentdestructorfunction"<Son(){
cout<"Sonconstructfunction"<Son(){
cout<"Sondestructorfunction"<main()
{
Parent*p=newSon();
deletep;
p=NULL;
return0;
}
//運行結果:
//Parentconstructfunction
//Sonconstructfunction
//Parentdestructorfunction
將基類的析構函數聲明為虛函數:
#include
usingnamespacestd;
classParent{
public:
Parent(){
cout<"Parentconstructfunction"<Parent(){
cout<"Parentdestructorfunction"<Son(){
cout<"Sonconstructfunction"<Son(){
cout<"Sondestructorfunction"<main()
{
Parent*p=newSon();
deletep;
p=NULL;
return0;
}
//運行結果:
//Parentconstructfunction
//Sonconstructfunction
//Sondestructorfunction
//Parentdestructorfunction
51. vector的底層原理
vector底層是一個動態數組,包含三個迭代器,start和finish之間是已經被使用的空間范圍,end_of_storage是整塊連續空間包括備用空間的尾部。
當空間不夠裝下數據(vec.push_back(val))時,會自動申請另一片更大的空間(1.5倍或者2倍),然后把原來的數據拷貝到新的內存空間,接著釋放原來的那片空間[vector內存增長機制]。
當釋放或者刪除(vec.clear())里面的數據時,其存儲空間不釋放,僅僅是清空了里面的數據。因此,對vector的任何操作一旦引起了空間的重新配置,指向原vector的所有迭代器會都失效了。
52. vector中的reserve和resize的區別
- reserve是直接擴充到已經確定的大小,可以減少多次開辟、釋放空間的問題(優化push_back),就可以提高效率,其次還可以減少多次要拷貝數據的問題。reserve只是保證vector中的空間大小(capacity)最少達到參數所指定的大小n。reserve()只有一個參數。
- resize()可以改變有效空間的大小,也有改變默認值的功能。capacity的大小也會隨著改變。resize()可以有多個參數。
53. vector中的size和capacity的區別
- size表示當前vector中有多少個元素(finish - start);
- capacity函數則表示它已經分配的內存中可以容納多少元素(end_of_storage - start);
54. vector中erase方法與algorithn中remove方法的區別
- vector中erase方法真正刪除了元素,迭代器不能訪問了
- remove只是簡單地將元素移到了容器的最后面,迭代器還是可以訪問到。因為algorithm通過迭代器進行操作,不知道容器的內部結構,所以無法進行真正的刪除。
55. vector迭代器失效的情況
- 當插入一個元素到vector中,由于引起了內存重新分配,所以指向原內存的迭代器全部失效。
- 當刪除容器中一個元素后,該迭代器所指向的元素已經被刪除,那么也造成迭代器失效。erase方法會返回下一個有效的迭代器,所以當我們要刪除某個元素時,需要it=vec.erase(it);。
56. 正確釋放vector的內存(clear(), swap(), shrink_to_fit())
- vec.clear():清空內容,但是不釋放內存。
- vector().swap(vec):清空內容,且釋放內存,想得到一個全新的vector。
- vec.shrink_to_fit():請求容器降低其capacity和size匹配。
- vec.clear();vec.shrink_to_fit();:清空內容,且釋放內存。
57. list的底層原理
- ist的底層是一個雙向鏈表,使用鏈表存儲數據,并不會將它們存儲到一整塊連續的內存空間中。恰恰相反,各元素占用的存儲空間(又稱為節點)是獨立的、分散的,它們之間的線性關系通過指針來維持,每次插入或刪除一個元素,就配置或釋放一個元素空間。
- list不支持隨機存取,如果需要大量的插入和刪除,而不關心隨即存取
58. 什么情況下用vector?什么情況下用list?什么情況下用deque?
- vector可以隨機存儲元素(即可以通過公式直接計算出元素地址,而不需要挨個查找),但在非尾部插入刪除數據時,效率很低,適合對象簡單,對象數量變化不大,隨機訪問頻繁。除非必要,我們盡可能選擇使用vector而非deque,因為deque的迭代器比vector迭代器復雜很多。
- list不支持隨機存儲,適用于對象大,對象數量變化頻繁,插入和刪除頻繁,比如寫多讀少的場景。
- 需要從首尾兩端進行插入或刪除操作的時候需要選擇deque。
59. priority_queue的底層原理
priority_queue:優先隊列,其底層是用堆來實現的。在優先隊列中,隊首元素一定是當前隊列中優先級最高的那一個。
60. map 、set、multiset、multimap的底層原理
map 、set、multiset、multimap的底層實現都是紅黑樹,epoll模型的底層數據結構也是紅黑樹,linux系統中CFS進程調度算法,也用到紅黑樹。
紅黑樹的特性:
- 每個結點或是紅色或是黑色;
- 根結點是黑色;
- 每個葉結點是黑的;
- 如果一個結點是紅的,則它的兩個兒子均是黑色;
- 每個結點到其子孫結點的所有路徑上包含相同數目的黑色結點。
61. 為什么map和set的插入刪除效率比其他序列容器高?
因為不需要內存拷貝和內存移動
62. 為什么map和set每次Insert之后,以前保存的iterator不會失效?
因為插入操作只是結點指針換來換去,結點內存沒有改變。而iterator就像指向結點的指針,內存沒變,指向內存的指針也不會變。
63. 當數據元素增多時(從10000到20000),map的set的查找速度會怎樣變化?
RB-TREE用二分查找法,時間復雜度為logn,所以從10000增到20000時,查找次數從log10000=14次到log20000=15次,多了1次而已。
64. map 、set、multiset、multimap的特點
- set和multiset會根據特定的排序準則自動將元素排序,set中元素不允許重復,multiset可以重復。
- map和multimap將key和value組成的pair作為元素,根據key的排序準則自動將元素排序(因為紅黑樹也是二叉搜索樹,所以map默認是按key排序的),map中元素的key不允許重復,multimap可以重復。
- map和set的增刪改查速度為都是logn,是比較高效的。
65. 為什么map和set的插入刪除效率比其他序列容器高?而且每次insert之后,以前保存的iterator不會失效?
- 存儲的是結點,不需要內存拷貝和內存移動。
- 插入操作只是結點指針換來換去,結點內存沒有改變。而iterator就像指向結點的指針,內存沒變,指向內存的指針也不會變。
66. 為什么map和set不能像vector一樣有個reserve函數來預分配數據?
在map和set內部存儲的已經不是元素本身了,而是包含元素的結點。也就是說map內部使用的Alloc并不是map
67. set的底層實現為什么不用哈希表而使用紅黑樹?
set中元素是經過排序的,紅黑樹也是有序的,哈希是無序的如果只是單純的查找元素的話,那么肯定要選哈希表了,因為哈希表在的最好查找時間復雜度為O(1),并且如果用到set中那么查找時間復雜度的一直是O(1),因為set中是不允許有元素重復的。而紅黑樹的查找時間復雜度為O(lgn)
68. hash_map與map有什么區別?什么時候用hash_map?什么時候用map?
- 構造函數:hash_map需要hash function和等于函數,而map需要比較函數(大于或小于)。
- 存儲結構:hash_map以hashtable為底層,而map以RB-TREE為底層。
- 總的說來,hash_map查找速度比map快,而且查找速度基本和數據量大小無關,屬于常數級別。而map的查找速度是logn級別。但不一定常數就比log小,而且hash_map還有hash function耗時。
- 如果考慮效率,特別當元素達到一定數量級時,用hash_map。
- 考慮內存,或者元素數量較少時,用map。
69. 迭代器失效的問題
插入操作:
- 對于vector和string,如果容器內存被重新分配,iterators,pointers,references失效;如果沒有重新分配,那么插入點之前的iterator有效,插入點之后的iterator失效;
- 對于deque,如果插入點位于除front和back的其它位置,iterators,pointers,references失效;當我們插入元素到front和back時,deque的迭代器失效,但reference和pointers有效;
- 對于list和forward_list,所有的iterator,pointer和refercnce有效。刪除操作:
- 對于vector和string,刪除點之前的iterators,pointers,references有效;off-the-end迭代器總是失效的;
- 對于deque,如果刪除點位于除front和back的其它位置,iterators,pointers,references失效;當我們插入元素到front和back時,off-the-end失效,其他的iterators,pointers,references有效;
- 對于list和forward_list,所有的iterator,pointer和refercnce有效。
- 對于關聯容器map來說,如果某一個元素已經被刪除,那么其對應的迭代器就失效了,不應該再被使用,否則會導致程序無定義的行為。
70. STL線程不安全的情況
- 在對同一個容器進行多線程的讀寫、寫操作時;
- 在每次調用容器的成員函數期間都要鎖定該容器;
- 在每個容器返回的迭代器(例如通過調用begin或end)的生存期之內都要鎖定該容器;
-
在每個在容器上調用的算法執行期間鎖定該容器。
-
C語言
+關注
關注
180文章
7616瀏覽量
137949 -
C++
+關注
關注
22文章
2114瀏覽量
73922 -
線程
+關注
關注
0文章
507瀏覽量
19766
原文標題:吐血整理!70道C語言與C++常見問答題
文章出處:【微信號:gh_c472c2199c88,微信公眾號:嵌入式微處理器】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
源代碼加密、源代碼防泄漏c/c++與git服務器開發環境
![源代碼加密、源代碼防泄漏<b class='flag-5'>c</b>/<b class='flag-5'>c++</b>與git服務器開發環境](https://file1.elecfans.com/web2/M00/B2/A6/wKgaomVuxEeAGSf1AABba4Y-Iak289.png)
Spire.XLS for C++組件說明
![Spire.XLS for <b class='flag-5'>C++</b>組件說明](https://file1.elecfans.com/web3/M00/05/E7/wKgZO2eFwUuAbuoQAAAbn_khf8A091.png)
AKI跨語言調用庫神助攻C/C++代碼遷移至HarmonyOS NEXT
同樣是函數,在C和C++中有什么區別
C++中實現類似instanceof的方法
![<b class='flag-5'>C++</b>中實現類似instanceof的方法](https://file1.elecfans.com/web2/M00/FE/0C/wKgaomaYe1CAQ31QAAAnf0IkoSU605.png)
SEGGER編譯器優化和安全技術介紹 支持最新C和C++語言
![SEGGER編譯器優化和安全技術介紹 支持最新<b class='flag-5'>C</b>和<b class='flag-5'>C++</b><b class='flag-5'>語言</b>](https://file1.elecfans.com/web2/M00/EC/80/wKgaomZew0OAa4J1AAAcf4qxt2I454.png)
C/C++中兩種宏實現方式
鴻蒙OS開發實例:【Native C++】
![鴻蒙OS開發實例:【Native <b class='flag-5'>C++</b>】](https://file1.elecfans.com/web2/M00/C8/31/wKgZomYZMTCAaDv3AAY5x13C324319.jpg)
使用 MISRA C++:2023? 避免基于范圍的 for 循環中的錯誤
![使用 MISRA <b class='flag-5'>C++</b>:2023? 避免基于范圍的 for 循環中的錯誤](https://file1.elecfans.com/web2/M00/A9/66/wKgZomUl7m-AHJX6AABuJjgxs14678.png)
為什么很少用C++開發單片機
![為什么很少用<b class='flag-5'>C++</b>開發單片機](https://file1.elecfans.com/web2/M00/C6/8F/wKgaomYBGYeAEfT2AAAsOmCJ4d8781.png)
C語言#define的應用
![<b class='flag-5'>C</b><b class='flag-5'>語言</b>#define的應用](https://file1.elecfans.com/web2/M00/C3/B9/wKgaomXn42eACLumAAAag1PydyU214.png)
評論