C++高頻面試問題

C++常見面試問題彙總

一、指針和引用的區別

1.指針有自己的一塊空間,而引用只是一個別名;
2.使用sizeof看一個指針的大小是4,而引用則是被引用對象的大小;
3.指針可以被初始化爲NULL,而引用必須被初始化且必須是一個已有對象 的引用;
4.作爲參數傳遞時,指針需要被解引用纔可以對對象進行操作,而直接對引 用的修改都會改變引用所指向的對象;
5.可以有const指針,但是沒有const引用;
6.指針在使用中可以指向其它對象,但是引用只能是一個對象的引用,不能 被改變;
7.指針可以有多級指針(**p),而引用只有一級;
8.指針和引用使用++運算符的意義不一樣;
(指針做自增運算是指向後面的內存,引用相當於一個別名,引用做自增運算就是對原對象做自增運算)
9.如果返回動態內存分配的對象或者內存,必須使用指針,引用可能引起內存泄露。

二、堆和棧的區別

1.堆棧空間分配的區別
棧:由操作系統自動分配和釋放,存放函數的參數值,局部變量的值等,其操作方法類似於數據結構中的棧。

堆:一般由程序員分配釋放,若程序員不釋放,程序結束時可能由OS回收,分配方式類似於鏈表

2.堆棧緩存方式的區別
棧使用的是一級緩存,他們通常都是被調用時處於存儲空間中,調用完畢立即釋放。

堆則是存放在二級緩存中,生命週期由虛擬機的垃圾回收算法來決定,所以調用這些對象的速度要低一些

3.堆棧數據結構的區別
棧是一種先進後出的數據結構
堆可以被看作一棵樹

三、new和delete是如何實現的,new 與 malloc的異同處

new/new[]和delete/delete[]是操作符;是C++用來實現動態內存管理的操作符;

new/new[]操作符是用來申請空間的;
delete/delete[]操作符是用來釋放動態申請出來的空間

—new和delete的實現原理
new在底層調用operator new全局函數來申請空間;
delete在底層通過operator delete全局函數來釋放空間;

/*該函數實際通過malloc來申請空間,申請成功時直接返回,申請空間失敗,嘗試
執行空 間不足應對措施,如果改應對措施用戶設置了,則繼續申請,否則拋異常。*/

void *_CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc){
	void *p;
	while((p=malloc(size))==0)
	if(_callnewh(size)==0){
		static const std::bad_alloc nomem;
		——RAISE(nomen);
}
	return (p);
}
void oprator delete(void *pUserData){
	_CrtMemBlockHeader *pHead;

	RTCCALLBACK(_RTC_Free_hook,(pUserData,0));

	if(pUserData==NULL)
		return ;
	
	_mlock(_HEAP_LOCK);
	_TRY
		phead =pHdr(pUserData);

		_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));
		
		_free_dbg( pUserData, pHead->nBlockUse );
	
__FINALLY
		_munlock(_HEAP_LOCK); 
__END_TRY_FINALLY
	
	 return;

}

#define free(p) _free_dbg(p, _NORMAL_BLOCK)

通過這兩個全局函數的實現,知道了operator new 實際是通過malloc來申請空間的,operator delete實際是通過free來釋放空間的;

—new和malloc的區別
1.malloc與free是C語言的標準函數,new/delete是C++的運算符

2.他們都可用於申請動態內存和釋放內存,new和delete在對象創建的時候自動執行構造函數,對象消亡之前會自動執行析構函數

3.new返回指定類型的指針,並且可以自動計算所需的大小
malloc必須用戶指定大小,並且默然返回類型爲void*,必須強行轉換爲實際類型的指針。

四、C和C++的區別

1.C是面向過程的,C++是面向對象的
2.C中的const和C++中的const的用法不同。

-與C語言不同,C++const不是隻讀變量,c語言中const實質上是隻讀變量
-C++中的const是一個真正意義上的常量
-C++編譯器可能會爲const分配空間
-c語言中的const變量是可以通過指針修改,而C++不行

3.C中的static和C++中的static的用法不同。
4.C中函數不能進行重載,C++中進行函數重載。
5.C中不能設置函數默認值,C++中可以設置。(返回值也不同,c中沒有返回值類型默認爲int,C++中則會報錯)
6.C++中有內聯函數
7.C中動態申請內存用的是malloc/free,C++中用的是new/delete。
8.C中函數傳參的方式有傳指針和傳值,C++中多了個傳引用(底層還是傳指針)。
9.C中作用域是全局和局部,C++中多了個命名空間和類內作用域。

五、Struct和class的區別

1.默認的繼承訪問權不同。class默認的是private,struct默認的是public

2.默認訪問權限。struct作爲數據結構的實現體,它默認的數據訪問控制是public,而class作爲對象的實現體,它默認的成員變量訪問控制是private的

3.class這個關鍵字還用於定義模板參數,就像“typename”,但關鍵字“struct”不用於定義模板參數

從上面的區別,我們可以看出,struct更適合看成是一個數據結構的實現體,class更適合看成是一個對象的實現體。

六、define 和const的區別

1.就起作用的階段而言:#define是在編譯的預處理階段起作用,而const是在編譯、運行的時候起作用

2.就起作用的方式而言:#define只是簡單的字符串替換,沒有類型檢查,而const有對應的數據類型,是要進行判斷的,可以避免一些低級的錯誤

3.就存儲方面而言:#define只是進行展開,有多少地方使用,就替換多少次,它定義的宏常量在內存中有若干個備份,const定義的只讀變量在程序運行過程中只有一份備份

4.從代碼調試的方便程度而言:const常量可以進行調試,define是不能進行調試的,因爲在預編譯階段就已經替換掉了

七、在C++中const和static的用法

1.static的作用
**-可以隱藏:**全局靜態變量只能在本文件內使用
**-默認初始化:**默認初始化爲0

變量
全局變量:修飾的全局變量,指定其內部鏈接,也就是隻能本文件使用。

局部變量:修飾的局部變量,改變其生命週期,並不會修改器作用域。

成員變量:只屬於類,不屬於對象。使用的適合可以通過類名或者對象引用。修飾的成員變量必須在類外單獨初始化,如果同時被const 修飾則可以在定義的適合進行初始化。

函數
普通函數:修飾的普通函數,指定其內部鏈接,也就是隻能本文可見。

類成員函數:靜態成員函數只屬於類,不屬於對象。沒有this指針,所以它不能訪問非靜態成員函數 ,和非靜態成員變量。 它是用來處理靜態成員數據,如果我們非要使用靜態成員函數訪問非靜態成員函數或者非靜態成員變量,我們可以間接使用類進行引用。

2.const的作用
修飾變量:c語言中const將一個變量轉化爲常變量,存儲在靜態文本段,只有讀取權限,C++中同樣會將一個變量轉化成常量,C++會對其進行優化,將其放入寄存器中,如果想去內存中讀取該數據時,我們可以使用volatile關鍵字進行修飾,保證其可見性。

修飾指針變量:如果const位於* 左側時,不能修改指針所指的對象(但是可以改變內容),如果const位於* 右側時,不能修改指針的指向,所以必須初始化。

修飾參數:作用是原參數在該函數中不可被改變。

修飾的返回值:是用const來修飾返回的指針或引用,保護指針指向的內容或引用的內容不被修改,也常用於運算符重載。歸根究底就是使得函數調用表達式不能作爲左值。

修飾成員變量:該變量只能在初始化列表裏初始化。

修飾成員函數:在成員函數後面加上const,const修飾this指針所指的的對象,也就是保證調用該成員函數的對象,在成員函數內部不會改變。(改變權限,權限可縮小,但是不可擴大)

C++的頂層const和底層const

1.指向常量的指針:代表不能改變其指向內容的指針,聲明時const可以放在類型名前後都可,聲明時:const int和int const是等價的,聲明指向常量的指針也就是底層const

int num_a = 1;
int const  *p_a = &num_a; //底層const
//*p_a = 2;  //錯誤,指向“常量”的指針不能改變所指的對象

注意:指向“常量”的指針不代表它所指向的內容一定是常量,只是代表不能通過解引用符(操作符*)來改變它所指向的內容。上例中指針p_a指向的內容就不是常量,可以通過賦值語句:num_a=2; 來改變它所指向的內容。

2.指針常量:代表指針本身是常量,聲明時必須初始化,之後它存儲的地址值就不能再改變,const必須放在指針符號後面,即const,聲明常量指針就是頂層const

int num_b = 2;
int *const p_b = &num_b; //頂層const
//p_b = &num_a;  //錯誤,常量指針不能改變存儲的地址值

區分頂層const和底層const的作用
1.執行對象拷貝時有限制,常量的底層const不能賦值給非常量的底層const

int num_c = 3;
const int *p_c = &num_c;  //p_c爲底層const的指針
//int *p_d = p_c;  //錯誤,不能將底層const指針賦值給非底層const指針
const int *p_d = p_c; //正確,可以將底層const指針複製給底層const指針

2.使用強制類型轉換函數const_cast時,需要能夠分辨底層const和頂層const,因爲const_cast只能改變運算對象的底層const

int num_e = 4;
const int *p_e = &num_e;
//*p_e = 5;  //錯誤,不能改變底層const指針指向的內容
int *p_f = const_cast<int *>(p_e);  //正確,const_cast可以改變運算對象的底層const。但是使用時一定要知道num_e不是const的類型。
*p_f = 5;  //正確,非頂層const指針可以改變指向的內容
cout << num_e;  //輸出5
發佈了26 篇原創文章 · 獲贊 9 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章