while循环中指针会自动释放吗_C++】C++常见面试题汇总_持续更新中...
1:指針(*)、引用(&)、解引用(*)、取地址(&)、的概念和區別
概念:
指針指向一塊內存,指針保存的是內存的地址;引用是變量的別名,本質是引用該變量的地址。
解引用是取指針指向的地址的內容,取地址是獲得變量在內存中的地址。
區別:
1引用使用是無需解引用,指針需解引用。
(2)引用不能為空,指針可以為空。
(3)引用在定義時被初始化一次,之后不可變;指針指向的值和本身的值是可變的,也就是說指針只是一塊地址,地址里的東西可變。
(4)程序會給指針變量分配內存區域,而引用不需要分配內存區域。
2:memset、memcpy和strcpy的區別
memcpy是內存拷貝函數,可以拷貝任何數據類型的對象,例如memcpy(b, a, sizeof(b))。
strcpy只能拷貝字符串,遇到’0′結束拷貝。
memset用來對一段內存空間全部設置為某個字符,例如:char a[100];memset(a, '', sizeof(a))。
3:struct和class的區別,struct與union的區別
struct和class都是聲明類的關鍵字
區別是:
4:指針在16位機、32位機、64位機分別占用多少個字節
16位機--2字節、32位機--4字節、64位機--8字節。
5:如何引用一個已經定義過的全局變量?區別是什么
如果在同一個文件中,直接引用即可。
如果不在同一個文件,有兩種方式:
(1)直接引用頭文件就可以使用了。
(2)用extern關鍵字重新聲明一下。
6:全局變量可不可以定義在可被多個.C文件包含的頭文件中?變量的聲明一般放在頭文件中,那么變的定義可以放在頭文件中嗎?在實際的編程中一般很少在頭文件中定義全局變量,因為多次引用可能重定義。7:do……while和while……do有什么區別?
do...while先執行循環再判斷條件,while...do先判斷條件再執行循環。
8:對于一個頻繁使用的短小函數,在C語言中應用什么實現,在C++中應用什么實現?
C用宏定義,C++用inline。9:main 函數執行以前,會執行什么代碼?
全局對象的構造函數會在main 函數之前執行,比如int a;初始化為0。
10:main 主函數執行完畢后,會執行什么代碼?
可以,使用on_exit 注冊的函數會在代碼執行完畢后執行:
void main( void )
{
String str(“zhanglin”);
on_exit( fn1 );
on_exit( fn2 );
on_exit( fn3 );
on_exit( fn4 );
printf( “This is executed first.” );
}
int fn1()
{
printf( “next.” );
return 0;
}
11:局部變量能否和全局變量重名?
可以,但是局部會屏蔽全局。要用全局變量,需要使用域作用符“::”。
12:描述內存分配方式以及它們的區別?
1.從靜態存儲區域分配。該存儲區域在程序編譯的時候就已經分配好了,這塊內存在程序的整個運行期間都存在。例如全局變量,static 變量。
2. 在棧上創建。在執行函數時,函數的局部變量存儲在該區域,函數執行結束時會釋放該存儲空間。棧內存分配運算內置于處理器的指令集。
3. 從堆上分配,亦稱動態內存分配。程序在運行的時候用malloc 或new 申請任意多少的內存,程序員自己負責在何時用free 或delete 釋放內存。 動態內存的生存期由程序員決定,使用非常靈活。
13:類的成員函數重載、覆蓋和隱藏的概念和區別?
14:static關鍵字?
15:const與#define的概念和優缺點?
const用來定義常量、修飾函數返回值,可以避免被修改,提高程序的健壯性。
define是宏定義,在編譯的時候會進行替換,這樣做的話可以避免沒有意義的數字或字符串,便于程序的閱讀。
區別:
1.const定義的數據有數據類型,而宏常量沒有數據類型。編譯器可以對const常量進行類型檢查。而對宏定義只進行字符替換,所以字符替換時可能出錯。
例子:
寫一個“標準”宏MIN,這個宏輸入兩個參數并返回較小的一個。
#define MIN(A,B) ((A) <= (B) (A) : (B))
least = MIN(a, b);
下面的關鍵字const是什么含意:
const int a;//a是一個常整型數
int const a;//a是一個常整型數
const int *a;//a是一個指向常整型數的指針
int * const a;//a是一個指向整型數的常指針
int const * a const;//a是一個指向常整型數的常指針
static關鍵字的作用:
(1)函數體內static變量的作用范圍為該函數體,不同于auto變量,該變量的內存只被分配一次,因此其值在下次調用時仍維持上 次的值;
(2)在模塊內的static全局變量可以被模塊內所用函數訪問,但不能被模塊外其它函數訪問;
(3)在模塊內的static函數只可被這一模塊內的其它函數調用,這個函數的使用范圍被限制在聲明它的模塊內;
(4)在類中的static成員變量屬于整個類所擁有,對類的所有對象只有一份拷貝;
(5)在類中的static成員函數屬于整個類所擁有,這個函數不接收this指針,因而只能訪問類的static成員變量。
const關鍵字的作用:
(1)欲阻止一個變量被改變,可以使用const關鍵字。在定義該const變量時,通常需要對它進行初始化,因為以后就沒有機會再去改變它了;
(2)對指針來說,可以指定指針本身為const,也可以指定指針所指的數據為const,或二者同時指定為const;
(3)在一個函數聲明中,const可以修飾形參,表明它是一個輸入參數,在函數內部不能改變其值;
(4)對于類的成員函數,若指定其為const類型,則表明其是一個常函數,不能修改類的成員變量;
(5)對于類的成員函數,有時候必須指定其返回值為const類型,以使得其返回值不為“左值”。例如:
16:堆棧溢出的原因?
沒有回收垃圾資源
棧溢出:
例如局部變量數組越界訪問或者函數內局部變量使用過多,超出了操作系統為該進程分配的棧的大小。
堆溢出:
所以溢出的原因可能是程序員申請了資源但是忘記釋放了。
17:刷新緩沖區方式?
換行刷新緩沖區:printf(“”);程序結束刷新緩沖區:return 0;18:類和對象的兩個基本概念?類的作用或概念:用來描述一組具有相似屬性的東西的對象的一種數據結構。類中有數據成員的聲明和定義,有成員函數的實現代碼。對象就是類的實例化。計算機中想要使用類,只能進行實例化。
19:介紹一下STL,詳細說明STL如何實現vector。
STL是標準模版庫,由容器算法迭代器組成。
STL有以下的一些優點:
(1)可以很方便的對一堆數據進行排序(調用sort());
(2)調試程序時更加安全和方便;
(3)stl是跨平臺的,在linux下也能使用。
vector實質上就是一個動態數組,會根據數據的增加,動態的增加數組空間。
什么是容器。如何實現?
容器是一種類類型,用來存儲數據
STL有7種主要容器:vector,list,deque,map,multimap,set,multiset.
20:變量的聲明和定義有什么區別
變量的聲明是告訴編譯器我有某個類型的變量,但是定義會分配了內存。
21:簡述#define #endif 和#ifndef的作用
這三個命令一般是為了避免頭文件被重復引用。
#ifndef CH_H //意思是如果沒有引用ch.h
#define CH_H //引用ch.h
#endif //否則不需要引用
22:引用與指針有什么區別?
(1) 引用必須被初始化,指針不必。
(2) 引用初始化以后不能被改變,指針可以改變所指的對象。
(3) 不存在指向空值的引用,但是存在指向空值的指針。
23:C++繼承機制?
n類成員的訪問控制方式
public:類本身、派生類和其它類均可訪問;
protected:類本身和派生類均可訪問,其它類不能訪問;
private(默認):類本身可訪問,派生類和其它類不能訪問。
繼承成員的訪問控制規則
——由父類成員的訪問控制方式和繼承訪問控制方式共同決定
private+public(protectd,private)=>不可訪問
pubic(protected)+public=>public(protected)
public(protected)+protected=>protected
public(protected)+private(默認)=>private
C++中的模板和virtual異同? ==>?
private繼承和public繼承區別? ==>?
24:什么是內存泄露?C++內存泄漏檢測內存泄露是指程序中動態分配了內存,但是在程序結束時沒有釋放這部分內存,從而造成那一部分內存不可用的情況
有一些內存泄漏的檢測工具,比如BoundsChecker。
靜態內存泄漏通過工具或者仔細檢查代碼找到泄漏點。
動態的內存泄漏很難查,一般通過在代碼中加斷點跟蹤和Run-Time內存檢測工具來查找。
內存泄漏的檢測可以分以下幾個步驟:
(1)看代碼new之后是否delete,就是申請了靜態內存用完是否釋放。看析構函數是否真的執行,如果沒有真正執行,就需要動態釋放對象;
(2)讓程序長時間運行,看任務管理器對應程序內存是不是一直向上增加;
(3)使用常用內存泄漏檢測工具來檢測內存泄漏點。
25:頭文件的作用是什么?
(1)頭文件用于保存程序的聲明。
(2)通過頭文件可以來調用庫函數。只要向用戶提供頭文件和二進制的庫即可。用戶只需要按照頭文件中的接口聲明來調用庫功能,編譯器會從庫中提取相應的代碼。
(3)如果某個接口被實現或被使用時,其方式與頭文件中的聲明不一致,編譯器就會指出錯誤,這一簡單的規則能大大減輕程序員調試、
27:system(”pause”)的作用?
調用DOS的命令,按任意鍵繼續,和getchar()差不多;省去了使用getchar();區別是一個屬于系統命令,一個屬于c++標準函數庫。
28:析構函數和虛函數的用法和作用?
29:編寫一個標準strcpy函數
可以拿10
char * strcpy( char *strDest, const char *strSrc )//為了實現鏈式操作,將目的地址返回,加3分!
{
assert( (strDest != NULL) &&(strSrc != NULL) );
char *address = strDest;
while( (*strDest++ = * strSrc++) != ‘0’ );
return address;
}
30:new、delete;malloc、free關系
new和delete是一組,new用調用構造函數來實例化對象和調用析構函數釋放對象申請的資源。
malloc和free是一對,malloc用來申請內存和釋放內存,但是申請和釋放的對象只能是內部數據類型。
區別:
malloc與free是C++/C語言的標準庫函數,new/delete是C++的運算符。
maloc/free只能操作內部數據類型。
31:delete與 delete []區別 都是用來調用析構函數的:
(1)delete只會調用一次析構函數,delete[]會調用每一個成員的析構函數。
(2)delete與new配套,delete []與new []配套,用new分配的內存用delete刪除用new[]分配的內存用delete[]刪除
32:繼承優缺點 優點:
繼承可以方便地改變父類的實現,可以實現多態,子類可以繼承父類的方法和屬性。
缺點:
破壞封裝,子類和父類可能存在耦合。
子類不能改變父類的接口。
33:C和C++有什么不同?
(1)c是面向過程的,也就是說更偏向邏輯設計;c++是面向對象的,提供了類,偏向類的設計。
(2)c適合要求代碼體積小的,效率高的場合,如比如嵌入式。
34:析構函數的調用次序,子類析構時要調用父類的析構函數嗎?
也就是說在基類的的析構調用的時候,派生類的信息已經全部銷毀了定義一個對象時先調用基類的構造函數、然后調用派生類的構造函數;
35:什么是“野指針”?野指針指向一個已刪除的對象或無意義地址的指針。與空指針不同,野指針無法通過簡單地判斷是否為 NULL避免,指針變量沒有被初始化,或者指針p被free或者delete之后,沒有置為NULL。
36:常量指針和指針常量的區別?常量指針:是一個指向常量的指針。可以防止對指針誤操作而修改該常量。
指針常量:是一個常量,且是一個指針。指針常量不能修改指針所指向的地址,一旦初始化,地址就固定了,不能對它進行移動操作。但是指針常量的內容是可以改變。
37:sizeof的概念(作用),舉例
38:如果NULL 和0 作為空指針常數是等價的, 那到底該用哪一個?
#define NULL 0
按道理說,null和0,沒有區別,但為何要多此一舉呢,
(1)什么是空指針常量?
0、0L、'0'、3 - 3、0 * 17 以及 (void*)0都是空指針常量。
(2)什么是空指針?
如果 p 是一個指針變量,則 p = 0;、p = 0L;、p = '0';、p = 3 - 3;、p = 0 * 17; 中的任何一種賦值操作之后, p 都成為一個空指針,由系統保證空指針不指向任何實際的對象或者函數。
(3)什么是 NULL?
即 NULL 是一個標準規定的宏定義,用來表示空指針常量。因此,除了上面的各種賦值方式之外,還可以用 p = NULL; 來使 p 成為一個空指針。
(4)空指針指向了內存的什么地方?
標準并沒有對空指針指向內存中的這個問題作出規定,一般取決于系統的實現。我們常見的空指針一般指向 0 地址,即空指針的內部用全 0 來表示。
空指針的“邏輯地址”一定是0,對于空指針的地址,操作系統是特殊處理的。并非空指針指向一個0地址的物理地址。
系統上空指針到底是一個 0指針還是非0地址,我們只需要了解一個指針是否是空指針就可以了——編譯器會自動實現其中的轉換,為我們屏蔽其中的實現細節。
(5)可以用 memset 函數來得到一個空指針嗎?
這個問題等同于:如果 p 是一個指針變量,那么memset( &p, 0, sizeof(p) ); 和 p = 0;是等價的嗎?
答案是否定的,雖然在大多數系統上是等價的,但是因為有的系統存在著“非零空指針” (nonzero null pointer),所以這時兩者不等價。
(6)可以定義自己的 NULL 的實現嗎?兼答"NULL 的值可以是 1、2、3 等值嗎?"類似問題
NULL 是標準庫中的一個符合上述條件的保留標識符。所以,如果包含了相應的標準頭文件而引入了 NULL 的話,則再在程序中重新定義 NULL 為不同的內容是非法的,其行為是未定義的。也就是說,如果是符合標準的程序,其 NULL 的值只能是 0,不可能是除 0 之外的其它值,比如 1、2、3 等。
(7)malloc 函數在分配內存失敗時返回 0 還是 NULL?
malloc 函數是標準 C 規定的庫函數。在標準中明確規定了在其內存分配失敗時返回的是空指針
39:如果NULL定義成#define NULL ((char *)0) 難道不就可以向函數傳入不加轉換的NULL了嗎?
不行。因為有的機器不同類型數據的指針有不同的內部表達。如果是字符指針的函數沒有問題, 但對于其它類型的指針參數仍然有問題, 而合法的構造如FILE *fp = NULL;則會失敗。
如果定義#define NULL ((void *)0)除了潛在地幫助錯誤程序運行以外, 這樣的定義還可以發現錯誤使用NULL 的程序。無論如何, ANSI 函數原型確保大多數指針參數在傳入函數時正確轉換。
40:使用非全零的值作為空指針內部表達的機器上, NULL是如何定義的?
跟其它機器一樣: 定義為0 。當程序員請求一個空指針時, 無論寫“0” 還是“NULL”, 都是有編譯器來生成適合機器的空指針的二進制表達形式。因此, 在空指針的內部表達不為0 的機器上定義NULL 為0 跟在其它機器上一樣合法:編譯器在指針上下文看到的未加修飾的0 都會被生成正確的空指針。
41:NULL 是什么, 它是怎么定義的?
很多人不愿意在程序中到處出現未加修飾的0。因此定義了預處理宏NULL為空指針常數, 通常是0 或者((void *)0) 。希望區別整數0 和空指針0 的人可以在需要空指針的地方使用NULL。
42:用“if(p)” 判斷空指針是否可靠?如果空指針的內部表達不是0 會怎么樣?
表達式中要求布爾值時:如果表達式等于0 則認為該值為假。if(p) 等價于if(p != 0)。
43:怎樣在程序里獲得一個空指針?
在指針上下文中的常數0 會在編譯時轉換為空指針。
char *p = 0;
if(p != 0)
44:空指針到底是什么?
空指針表示“未分配” 或者“尚未指向任何地方” 的指針。
空指針可以確保不指向任何對象或函數; 而未初始化指針則可能指向任何地方。
45:我能否用void** 指針作為參數, 使函數按引用接受一般指針?
C 中沒有一般的指針的指針類型。void* 可以用作一般指針只是因為當它和其它類型相互賦值的時候, 如果需要, 它可以自動轉換成其它類型; 如果試圖這樣轉換所指類型為void* 之外的類型的void** 指針時, 這個轉換不能完成。
46:我有一個char * 型指針剛好指向一些int 型變量, 我想跳過它們。 為什么((int *)p)++; 不行?
類型轉換的實質“把這些二進制位看作另一種類型, 并作相應的對待”; ((int *)p)++是一個轉換操作符, 根據定義它只能生成一個右值(rvalue)。而右值既不能賦值, 也不能用++ 自增。正確的做法:p = (char *)((int *)p + 1);
47:*p++ 自增p 還是p 所指向的變量?
*p++ 和*(p++) 等價。要自增p 指向的值, 使用(*p)++, 或者++*p。
48:我想聲明一個指針并為它分配一些空間,代碼char *p; *p = malloc(10)的問題;
你所聲明的指針是p, 而不是*p, 當你操作指針本身時, 你只需要使用指針的名字即可:p = malloc(10);
49:int i=7; printf(“%d”, i++ *i++);的值?
i++*i++=49
50:枚舉和#define 有什么不同?1):#define 是在預編譯階段進行簡單替換。枚舉常量則是在編譯的時候確定其值。
2):一般在編譯器里,可以調試枚舉常量,但是不能調試宏常量。
3):枚舉可以一次定義大量相關的常量,而#define 宏一次只能定義一個。
51:C++文件編譯與執行的四個階段
第一階段:預處理階段。根據文件中的預處理指令來修改源文件的內容。如#include指令,作用是把頭文件的內容添加到.cpp文件中。
第二階段:編譯階段,將其翻譯成等價的中間代碼或匯編代碼。
第三階段:匯編階段,把匯編語言翻譯成目標機器指令。
第四階段:是鏈接,例如,某個源文件中的函數可能引用了另一個源文件中定義的某個函數;在程序中可能調用了某個庫文件中的函數。
52:聲明struct x1 { . . . }; 和typedef struct { . . . } x2; 有什么不同?
第一種形式聲明了一個“結構標簽”;
第二種聲明了一個“類型定義”。
主要的區別是第一種方式定義結構體變量需要寫“struct x1”而引用第一種, 而第二種方式定義結構體變量不需要使用struct 關鍵字。
53:以下的初始化有什么區別?
char a[] = “string literal”; char *p= “string literal”
用作數組初始值, 它指明該數組中字符的初始值。
第二種情況會轉化為一個無名的靜態字符數組, 可能會存儲在只讀內存中, 這就是造成它不一定能被修改。第二個聲明把p 初始化成指向無名數組的第一個元素。為了編譯舊代碼, 有的編譯器有一個控制字符串是否可寫的開關。
54:對于沒有初始化的變量的初始值可以作怎樣的假定?如果一個全局變量初始值為“零”, 它可否作為空指針或浮點零?
對于具有“靜態” 生存期的未初始化全局變量可以確保初始值為零,如果是指針會被初始化為正確的空指針, 如果是浮點數會被初始化為0.0 。
對于局部變量,如果沒有顯示地初始化, 則包含的是垃圾內容。
用malloc() 和realloc() 動態分配的內存也可能包含垃圾數據, 因此必須由調用者正確地初始化。
55:函數指針的定義是什么?
是一個指向函數的指針。看例子:
A),char * (*fun1)(char * p1,char * p2);//fun1 不是函數名,而是一個指針變量,它指向一個函數。這個函數有兩個指針類型的參數,函數的返回值也是一個指針。
B),char * *fun2(char * p1,char * p2);//是個二級指針
C),char * fun3(char * p1,char * p2);//函數的返回值為char *類型
56:int *p = NULL 和*p = NULL 有什么區別?
int *p = NULL;//定義一個指針變量p,其指向的內存里面保存的是int 類型的數據;在定義變量p 的同時把p 的值設置為0×00000000,而不是把*p 的值設置為0×00000000
int *p;
*p = NULL;
給*p 賦值為NULL,即給p指向的內存賦值為NULL;但是由于p 指向的內存可能是非法的,所以調試的時候編譯器可
能會報告一個內存訪問錯誤。
int i = 10;
int *p = &i;
*p = NULL;
在編譯器上調試一下,我們發現p 指向的內存由原來的10 變為0 了;而p 本身的值, 即內存地址并沒有改變。
57:介紹一下#error 預處理
#error 預處理指令的作用是,編譯程序時,只要遇到#error 就會生成一個編譯錯誤提示消息,并停止編譯。其語法格式為:
#error error-message
注意,宏串error-message 不用雙引號包圍。遇到#error 指令時,錯誤信息被顯示,可能同時還顯示編譯程序作者預先定義的其他內容。
58:用變量a給出下面的定義
59:分別給出BOOL,int,float,指針變量 與“零值”比較的 if 語句(假設變量名為var)
BOOL型變量:if(!var)
int型變量: if(var==0)
float型變量:
const float EPSINON = 0.00001;
if ((x >= – EPSINON) && (x <= EPSINON)
指針變量: if(var==NULL)
60:什么是預編譯?何時需要預編譯?預編譯又稱為預處理 , 是做些代碼文本的替換工作。處理 # 開頭的指令 , 比如拷貝 #include 包含的文件代碼, #define 宏定義的替換 , 條件編譯等。
c 編譯系統在對程序進行通常的編譯之前,先進行預處理。 c 提供的預處理功能主要有以下三 種: 1 )宏定義 2 )文件包含 3 )條件編譯
61:內聯函數與宏有什么區別
內聯函數在編譯時展開,宏在預編譯時展開
在編譯的時候內聯函數可以直接被嵌入到目標代碼中,而宏只是一個簡單的文本替換
內聯函數可以完成諸如類型檢測、語句是否正確等編譯功能,宏就不具備這樣的功能
inline函數是函數,宏不是函數。
62:iostream與iostream.h的區別#include 非標準輸入輸出流
#include 標準輸入輸出流
有“.h”的就是非標準的,C的標準庫函數,無“.h”的,就要用到命令空間,是C++的。63:namespace的使用因為標準庫非常的龐大,所程序員在選擇的類的名稱或函數名時就很有可能和標準庫中的某個名字相同。所以為了避免這種情況所造成的名字沖突,就把標準庫中的一切都被放在名字空間std中。 C++標準程序庫中的所有標識符都被定義于一個名為std的namespace中。
1、直接指定標識符。例如std::ostream
2、使用using關鍵字。
using std::cout;
using std::endl;
3、最方便的就是使用using namespace std;
64:堆與棧的區別(1)一個是靜態的,一個是動態的,堆是靜態的,由用戶申請和釋放,棧是動態的,保存程序的局部變量(2)申請后系統的響應不同
棧:只要棧的剩余空間大于申請空間,系統就為程序提供內存,否則將拋出棧溢出異常
堆:當系統收到程序申請時,先遍歷操作系統中記錄空閑內存地址的鏈表,尋找第一個大于所申請空間的堆結點,然后將該結點從空間結點鏈表中刪除,并將該結點的空間分配給程序。
(3)申請大小限制的不同
棧:在windows下,棧的大小一般是2M,如果申請的空間超過棧的剩余空間時,將提示overflow。
堆:堆是向高地址擴展的數據結構,是不連續的內存區域。這是由于系統是用鏈表來存儲的空閑內存地址的,自然是不連續的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限于計算機系統中有效的虛擬內存。由此可見,堆獲得的空間比較靈活,也比較大。 65:含參數的宏與函數的優缺點
宏: 優點:在預處理階段完成,不占用編譯時間,同時,省去了函數調用的開銷,運行效率高
缺點:不進行類型檢查,多次宏替換會導致代碼體積變大,而且由于宏本質上是字符串替換,故可能會由于一些參數的副作用導致得出錯誤的結果。
函數:優點:有類型檢查,比較安全。缺點:函數調用需要參數、返回地址等的入棧、出棧開銷,效率沒有帶參數宏高
宏與內聯函數的區別
內聯函數和宏都是在程序出現的地方展開,內聯函數不是通過函數調用實現的,是在調用該函數的程序處將它展開(在編譯期間完成的);宏同樣是;
不同的是:內聯函數可以在編譯期間完成諸如類型檢測,語句是否正確等編譯功能;宏就不具有這樣的功能,而且宏展開的時間和內聯函數也是不同的(在運行期間展開)
66:多態的作用?
(1)可以隱藏實現的細節,使得代碼模塊化;方便擴展代碼;
(2)可以實現接口重用。
67: 類的靜態成員和非靜態成員有何區別?
靜態成員則是屬于這個類的非靜態成員是屬于每個對象的
68:C++純虛函數,虛函數,虛函數的實現,什么是虛指針?
純虛函數是在基類中聲明的虛函數,它在基類中沒有定義,但要求任何派生類都要定義自己的實現方法。
virtual void f()=0;//是一個接口,子類必須實現這個接口虛指針或虛函數指針是虛函數的實現細節。帶有虛函數的每一個對象都有一個虛指針指向該類的虛函數表。虛函數 :虛函數是在基類中被聲明為virtual,并在派生類中重新定義的成員函數,可實現成員函數的動態覆蓋(Override)
純虛函數和虛函數的區別是,純虛函數子類必須實現。 純虛函數的優點:(1)可以實現多態特性
(2)定義一個標準的接口,在派生類中必須予以重寫以實現多態性。
抽象類 :包含純虛函數的類稱為抽象類。由于抽象類包含了沒有定義的純虛函數,所以不能定義抽象類的對象。
多態性可分為兩類:靜態多態和動態多態。函數重載和運算符重載實現的多態屬于靜態多態,動態多態性是通過虛函數實現的。
虛函數與構造函數,析構函數,成員函數的關系
為什么基類析構函數是虛函數?
編譯器總是根據類型來調用類成員函數。但是一個派生類的指針可以安全地轉化為一個基類的指針。這樣刪除一個基類的指針的時候,C++不管這個指針指向一個基類對象還是一個派生類的對象,調用的都是基類的析構函數而不是派生類的。如果你依賴于派生類的析構函數的代碼來釋放資源,而沒有重載析構函數,那么會有資源泄漏。
為什么構造函數不能為虛函數
虛函數采用一種虛調用的方法。需調用是一種可以在只有部分信息的情況下工作的機制。如果創建一個對象,則需要知道對象的準確類型,因此構造函數不能為虛函數。
如果虛函數是有效的,那為什么不把所有函數設為虛函數?
不行。因為每個虛函數的對象都要維護一個虛函數表,因此在使用虛函數的時候都會產生一定的系統開銷,這是沒有必要的。
69:面向對象的三個基本特征,并簡單敘述之?
1. 封裝:將客觀事物抽象成類,每個類對自身的數據和方法。封裝可以使得代碼模塊化,目的是為了代碼重用
2. 繼承:子類繼承父類的方法和屬性,繼承可以擴展已存在的代碼,目的是為了代碼重用
3. 多態:允許將子類類型的指針賦值給父類類型的指針。
70:什么是“&(引用)”?申明和使用“引用”要注意哪些問題?
引用就是某個目標變量的“別名”。注意事項:(1)申明一個引用的時候,必須要對其進行初始化。(2)初始化后,該引用名不能再作為其他變量名的別名。(3)引用本身不占存儲單元,系統不給引用分配存儲單元。(4)返回引用時,在內存中不產生被返回值的副本
(5)不能返回局部變量的引用。主要原因是局部變量會在函數返回后被銷毀.
71:引用與多態的關系? 引用就是對象的別名。引用主要用作函數的形參。引用必須用與該引用同類型的對象初始化: 引用是除指針外另一個可以產生多態效果的手段。這意味著,一個基類的引用可以指向它的派生類實例。
int ival = 1024;int &refVal = ival; const 對象的引用只能是const類型的:const int ival = 1024;const int &refVal = ival; 多態是通過虛函數實現的。
72:指針和引用有什么區別;為什么傳引用比傳指針安全?如果我使用常量指針難道不行嗎?
(1) 引用在創建的同時必須初始化,保證引用的對象是有效的,所以不存在NULL引用;而指針在定義的時候不必初始化,所以,指針則可以是NULL,可以在定義后面的任何地方重新賦值。
(2) 引用一旦被初始化為指向一個對象,它就不能被改變為另一個對象的引用;而指針在任何時候都可以改變為指向另一個對象.
(3) 引用的創建和銷毀并不會調用類的拷貝構造函數
因為不存在空引用,并且引用一旦被初始化為指向一個對象,它就不能被改變為另一個對象的引用,所以比指針安全。
由于const 指針仍然存在空指針,并且有可能產生野指針,所以還是不安全
73:參數傳遞有幾種方式;實現多態參數傳遞采用什么方式,如果沒有使用某種方式原因是什么?
傳值,傳指針或者引用
74:拷貝構造函數相關問題,深拷貝,淺拷貝,臨時對象等。
深拷貝意味著拷貝了資源和指針,而淺拷貝只是拷貝了指針,沒有拷貝資源
這樣使得兩個指針指向同一份資源,可能造成對同一份析構兩次,程序崩潰。而且浪費時間,并且不安全。
臨時對象的開銷比局部對象小些。
75:構造函數的特點
構造函數只在建立對象的時候自動被調用一次
構造函數必須是公共的,否則無法生成對象
構造函數只負責為自己的類構造對象
在構造函數中初始化變量
Person::Person( ) : name(“Jack”), age(30)
{
…
}
76:面向對象如何實現數據隱藏
定義類來實現數據隱藏:
成員函數和屬性的類型:
私有成員private
保護成員protected
公共成員public
77:字符指針、浮點數指針、以及函數指針這三種類型的變量哪個占用的內存最大?為什么?
所有指針變量占用內存單元的數量都是相同的。
78:C++是不是類型安全的?
不是。兩個不同類型的指針之間可以強制轉換.
79:const char*, char const*, char *const的區別是什么?
把一個聲明從右向左讀,* 讀成指向
char * const cp;//cp是常指針,指向char類型的數據
const char * cp;//cp是char類型的指針,指向const char
char const * p;//C++里面沒有const*的運算符,所以const屬于前面的類型。
80:什么是模板和宏?模板怎么實現?模板有什么缺點和優點?模版特化的概念,為什么特化?
標準庫大量采用了模板技術。比如容器。
模板是一個藍圖,它本身不是類或函數。編譯器用模板產生指定的類或函數的特定類型版本。模版的形參分為類型形參和非類型形參類型形參就是表示類型的形參,跟在關鍵字typename后非類型形參用來表示常量表達式
81:空指針和懸垂指針的區別?
空指針是指被賦值為NULL的指針;delete指向動態分配對象的指針將會產生懸垂指針。
空指針可以被多次delete,而懸垂指針再次刪除時程序會變得非常不穩定;
使用空指針和懸垂指針都是非法的,而且有可能造成程序崩潰,如果指針是空指針,盡管同樣是崩潰,但和懸垂指針相比是一種可預料的崩潰。
(a)指針數組和數組指針,函數指針和指針函數相關概念
指針數組:用于存儲指針的數組
int* a[4]
元素表示:*a[i]
數組指針:指向數組的指針
int (*a)[4]
元素表示:(*a)[i]
指針函數:函數返回類型是某一類型的指針,int *f(x,y);
指針函數與函數指針表示方法的不同。最簡單的辨別方式就是看函數名前面的指針*號有沒有被括號()包含,如果被包含就是函數指針,反之則是指針函數。
函數指針:是指向函數的指針變量,即本質是一個指針變量。
int (*f) (int x); /* 聲明一個函數指針 */ 類型說明符 (*指針的變量名)(參數)
f=func; /* 將func函數的首地址賦給指針f */
指向函數的指針包含了函數的地址
指針的指針: 例如:char ** cp;
如果有三個星號,那就是指針的指針的指針,依次類推。 指針的指針需要用到指針的地址。
char c='A';
char *p=&c;
char **cp=&p;
通過指針的指針,不僅可以訪問它指向的指針,還可以訪問它指向的指針所指向的數據:
char *p1=*cp;
char c1=**cp; 指向指針數組的指針: char *Names[]={ Bill,Sam,0};
char **nm=Names;
while(*nm!=0) printf(%s,*nm++);
先用字符型指針數組Names的地址來初始化指針nm。每次printf()的調用都首先傳遞指針nm指向的字符型指針,然后對nm進行自增運算使其指向數組的下一個元素(還是指針)。
82:什么是智能指針?
當類中有指針成員時,一般有兩種方式來管理指針成員:
(1)每個類對象都保留一份指針指向的對象的拷貝;
(2)使用智能指針,從而實現指針指向的對象的共享。實質是使用計數器與對象相關聯,這樣做可以保證對象正確的刪除,避免垂懸指針。
每次創建類的新對象時,初始化指針并將引用計數置為1;當對象作為另一對象的副本而創建時,拷貝構造函數拷貝指針并增加與之相應的引用計數;對一個對象進行賦值時,賦值操作符減少左操作數所指對象的引用計數,并增加右操作數所指對象的引用計數;調用析構函數時,構造函數減少引用計數。
83:C++空類默認有哪些成員函數?
默認構造函數、析構函數、復制構造函數、賦值函數
84:哪一種成員變量可以在一個類的實例之間共享?
答:static靜態成員變量
85:什么是多態?多態有什么作用?如何實現的?多態的缺點?
多態就是一個接口,多種方法。所以說,多態的目的則是為了實現接口重用。也就是說,不論傳遞過來的究竟是那個類的對象,函數都能夠通過同一個接口調用到適應各自對象的實現方法。
C++的多態性是通過虛函數來實現的,虛函數允許子類重新定義成員函數,而子類重新定義父類的做法稱為覆蓋(override),或重寫。而重載則是允許有多個同名的函數,而這些函數的參數列表不同,允許參數個數不同,參數類型不同。編譯器會根據函數列表的不同,而生成一些不同名稱的預處理函數,來實現同名函數的重載。但這并沒有體現多態性。
多態與非多態的實質區別就是函數的地址是運行時確定還是編譯時確定。如果函數的調用在編譯器編譯期間就可以確定函數的調用地址,并生產代碼,是靜態的。而如果函數調用的地址在運行時才確定,就是動態的。
最常見的用法就是聲明基類的指針,利用該指針指向任意一個子類對象,調用相應的虛函數,可以根據指向的子類的不同而實現不同的方法。如果沒有使用虛函數的話,即沒有利用C++多態性,則利用基類指針調用相應的函數的時候,將總被限制在基類函數本身,而無法調用到子類中被重寫過的函數
86:虛函數表解析和內存布局
虛函數表
虛函數是通過一張虛函數表來實現的。就像一個地圖一樣,指明了實際所應該調用的函數的地址。
這里我們著重看一下這張虛函數表。C++的編譯器保證了虛函數表的指針存在于對象實例中最前面的位置(為了性能)。因此我們可以通過對象實例的地址得到這張虛函數表,然后通過遍歷其中函數指針,并調用相應的函數。
為什么可以由父類的指針調用子類的對象的虛函數:
Derive d;//Derive 是Base的子類
Base *b1 = &d;//這必須使用父類的指針???
b1->f(); //Derive::f()
87:公有繼承、受保護繼承、私有繼承
1)公有繼承時,派生類對象可以訪問基類中的公有成員,派生類的成員函數可以訪問基類中的公有和受保護成員;公有繼承時基類受保護的成員,可以通過派生類對象訪問但不能修改。
2)私有繼承時,基類的成員只能被直接派生類的成員訪問,無法再往下繼承;
3)受保護繼承時,基類的成員也只被直接派生類的成員訪問,無法再往下繼承。
88:有哪幾種情況只能用構造函數初始化列表而不能用賦值初始化?
答:const成員,引用成員
89:C++如何阻止一個類被實例化?一般在什么時候將構造函數聲明為private?
1)將類定義為抽象基類或者將構造函數聲明為private;
2)不允許類外部創建類對象,只能在類內部創建對象
90:類使用static成員的優點,如何訪問?
(1)static 成員的名字是在類的作用域中,因此可以避免與其他類的成員或全局對象名字沖突;
(2)可以實施封裝。static 成員可以是私有成員,而全局對象不可以;
(3) static 成員是與特定類關聯的,可清晰地顯示程序員的意圖。
91:static數據成員和static成員函數
(1)static數據成員:
static數據成員獨立于該類的任意對象而存在;static數據成員(const static數據成員除外)在類定義體內聲明,必須在類外進行初始化。不像普通數據成員,static成員不能在類的定義體中初始化,只能在定義時才初始化。 static數據成員定義放在cpp文件中,不能放在初始化列表中。Const static成員可就地初始化。
變量定義:用于為變量分配存儲空間,還可為變量指定初始值。程序中,變量有且僅有一個定義。
變量聲明:用于向程序表明變量的類型和名字。
(2)static成員函數: 在類的外部定義,Static成員函數沒有this形參,它可以直接訪問所屬類的static成員,不能直接使用非static成員。因為static成員不是任何對象的組成部分,所以static成員函數不能被聲明為const。同時,static成員函數也不能被聲明為虛函數。
92:C++的內部連接和外部連接
編譯單元:當編譯cpp文件時,預處理器首先遞歸包含頭文件,形成一個編譯單元。這個編譯單元會被編譯成為一個與cpp文件名同名的目標文件(.o或是.obj)。連接程序把不同編譯單元中產生的符號聯系起來,構成一個可執行程序。
內部連接:如果一個名稱對于它的編譯單元來說是局部的,并且在連接時不會與其它編譯單元中的同樣的名稱相沖突,那么這個名稱有內部連接:
a)所有的聲明
b)名字空間(包括全局名字空間)中的靜態自由函數、靜態友元函數、靜態變量的定義
c)enum定義
d)inline函數定義(包括自由函數和非自由函數)
e)類的定義
f)名字空間中const常量定義
g)union的定義
外部連接:在一個多文件程序中,如果一個名稱在連接時可以和其它編譯單元交互,那么這個名稱就有外部連接。
以下情況有外部連接:
a)類非inline函數總有外部連接。包括類成員函數和類靜態成員函數
b)類靜態成員變量總有外部連接。
c)名字空間(包括全局名字空間)中非靜態自由函數、非靜態友元函數及非靜態變量
93:變量的分類,全局變量和局部變量有什么區別?實怎么實現的?操作系統和編譯器是怎么知道的?static全局變量與普通的全局變量有什么區別?static局部變量和普通局部變量有什么區別?static函數與普通函數有什么區別?
1)變量可以分為:全局變量、局部變量、靜態全局變量、靜態局部變量
靜態全局變量只在定義它的文件內有效局部變量在定義它的函數內有效,這個函數返回會后失效。
靜態局部變量只在定義它的函數內有效,只是程序僅分配一次內存,函數返回后,該變量不會消失,直到程序運行結束后才釋放;全局變量和靜態變量如果沒有手工初始化,則由編譯器初始化為0。局部變量的值不可知。
變量的作用域:
形參變量只在被調用期間才分配內存單元,調用結束立即釋放。 這一點表明形參變量只有在函數內才是有效的, 離開該函數就不能再使用了。局部變量也稱為內部變量。其作用域僅限于函數內, 離開該函數后再使用這種變量是非法的。
全局變量也稱為外部變量,它不屬于哪一個函數,它屬于一個源程序文件。其作用域是整個源程序。在函數中使用全局變量,一般應作全局變量說明。 只有在函數內經過說明的全局變量才能使用。全局變量的說明符為extern。 但在一個函數之前定義的全局變量,在該函數內使用可不再加以說明。 對于全局變量還有以下幾點說明:
外部變量可加強函數模塊之間的數據聯系, 但是又使函數要依賴這些變量,因而使得函數的獨立性降低。從模塊化程序設計的觀點來看這是不利的, 因此在不必要時盡量不要使用全局變量。
在同一源文件中,允許全局變量和局部變量同名。在局部變量的作用域內,全局變量不起作用。
變量的存儲方式可分為“靜態存儲”和“動態存儲”兩種。
靜態存儲變量通常是在變量定義時就分定存儲單元并一直保持不變, 直至整個程序結束。動態存儲變量是在程序執行過程中,使用它時才分配存儲單元, 使用完畢立即釋放。 如果一個函數被多次調用,則反復地分配、 釋放形參變量的存儲單元。從以上分析可知, 靜態存儲變量是一直存在的, 而動態存儲變量則時而存在時而消失。我們又把這種由于變量存儲方式不同而產生的特性稱變量的生存期。 生存期表示了變量存在的時間。 生存期和作用域是從時間和空間這兩個不同的角度來描述變量的特性,這兩者既有聯系,又有區別。 一個變量究竟屬于哪一種存儲方式, 并不能僅從其作用域來判斷,還應有明確的存儲類型說明。
從作用域看:
全局變量具有全局作用域。全局變量只需在一個源文件中定義,就可以作用于所有的源文件。當然,其他不包含全局變量定義的源文件需要用extern 關鍵字再次聲明這個全局變量。
靜態局部變量具有局部作用域,它只被初始化一次,自從第一次被初始化直到程序運行結束都一直存在,它和全局變量的區別在于全局變量對所有的函數都是可見的,而靜態局部變量只對定義自己的函數體始終可見。
局部變量也只有局部作用域,它是自動對象(auto),它在程序運行期間不是一直存在,而是只在函數執行期間存在,函數的一次調用執行結束后,變量被撤銷,其所占用的內存也被收回。
靜態全局變量也具有全局作用域,它與全局變量的區別在于如果程序包含多個文件的話,它作用于定義它的文件里,不能作用到其它文件里,即被static關鍵字修飾過的變量具有文件作用域。這樣即使兩個不同的源文件都定義了相同名字的靜態全局變量,它們也是不同的變量。
從分配內存空間看:
全局變量,靜態局部變量,靜態全局變量都在靜態存儲區分配空間,而局部變量在棧里分配空間。
全局變量本身就是靜態存儲方式,靜態全局變量當然也是靜態存儲方式。這兩者在存儲方式上并無不同。這兩者的區別雖在于非靜態全局變量的作用域是整個源程序,當一個源程序由多個源文件組成時,非靜態的全局變量在各個源文件中都是有效的。而靜態全局變量則限制了其作用域,即只在定義該變量的源文件內有效,在同一源程序的其它源文件中不能使用它。由于靜態全局變量的作用域局限于一個源文件內,只能為該源文件內的函數公用,因此可以避免在其它源文件中引起錯誤。
1)、靜態變量會被放在程序的靜態數據存儲區(全局可見)中,這樣可以在下一次調用的時候還可以保持原來的賦值。這一點是它與堆棧變量和堆變量的區別。
2)、變量用static告知編譯器,自己僅僅在變量的作用范圍內可見。這一點是它與全局變量的區別。
程序的局部變量存在于(堆棧)中,全局變量存在于(靜態區 )中,動態申請數據存在于( 堆)中。
94:應用程序在運行時的內存包括代碼區和數據區,其中數據區又包括哪些部分?
對于一個進程的內存空間而言,可以在邏輯上分成 3個部份:代碼區,靜態數據區和動態數據區。
動態數據區一般就是“堆棧”。 棧是一種線性結構,堆是一種鏈式結構。進程的每個線程都有私有的“棧”。
全局變量和靜態變量分配在靜態數據區,本地變量分配在動態數據區,即堆棧中。程序通過堆棧的基地址和偏移量來訪問本地變量。
95:C++里面是不是所有的動作都是main()引起的?如果不是,請舉例。
比如全局變量的初始化,就不是由main函數引起的:
class A{};
A a; //a的構造函數限執行
int main() {}
96:異常框架
異常存在于程序的正常功能之外,并要求程序立即處理。 C++ 的異常處理包括:
1. throw 表達式,錯誤檢測部分使用這種表達式來說明遇到了不可處理的錯誤。
2. try 塊,錯誤處理部分使用它來處理異常。try 語句塊以 try 關鍵字開 始,并以一個或多個 catch 子句結束。在 try 塊中執行的代碼所拋出 (throw)的異常,通常會被其中一個 catch 子句處理。
3. 由標準庫定義的一組異常類,用來在 throw 和相應的 catch 之間傳遞有關的錯誤信息。 throw 表達式:if (!item1.same_isbn(item2))throw runtime_error("Data must refer to same ISBN"); try 塊:try {program-statements} catch (exception-specifier) {handler-statements} catch (exception-specifier) {handler-statements} 函數在尋找處理代碼的過程中退出在復雜的系統中,程序的執行路徑也許在遇到拋出異常的代碼之前,就已經經過了多個 try 塊。拋出一個異常時,首先要搜索 的是拋出異常的函數。如果沒有找到匹配的 catch,則終止這個函數的執行,并在調用這個函數的函數中尋找相配的 catch。如果仍然沒有找到相應的處理代碼,該函數同樣要終止,搜索調用它的函數。直到找到適當類型的 catch 為止。
總結
以上是生活随笔為你收集整理的while循环中指针会自动释放吗_C++】C++常见面试题汇总_持续更新中...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 转 Intellij中的常用快捷键
- 下一篇: c++ 操作oracle 最佳方式_or