C++ 面試題
轉載自:https://www.cnblogs.com/liufei1983/p/7099401.html
1 new/delete 與 malloc/free的區別
運算子是語言自身的特性,有固定的語義,編譯器知道意味著什麼,由編譯器解釋語義,生成相應的程式碼。
庫函式是依賴於庫的,一定程度上獨立於語言的。編譯器不關心庫函式的作用,只保證編譯,呼叫函式引數和返回值符合語法,生成call函式的程式碼。
實際中,一些高階點的編譯器,都會對庫函式進行特別處理。
malloc/free是庫函式,new/delete是C++運算子。對於非內部資料型別而言,光用malloc/free無法滿足動態物件都要求。new/delete是運算子,編譯器保證呼叫構造和解構函式對物件進行初始化/析構。但是庫函式malloc/free是庫函式,不會執行構造/析構。
2 delete與delete[ ] 區別
delete只會呼叫一次解構函式,而delete[] 會呼叫沒一個成員的解構函式。
delete 與 new 配套使用; delete[] 與new[]配套使用。
對於內建的簡單資料型別,delete和delete[] 功能相同。
對於複雜資料型別,delete和delete[]不同,前者刪除單個物件,後者刪除陣列。
3 子類析構時,要呼叫父類的解構函式嗎?
不用顯示呼叫,自動呼叫。
4 多型, 虛擬函式, 純虛擬函式
多型:不同物件接收相同的訊息產生不同的動作。多型包括 編譯時多型和 執行時多型
執行時多型是:通過繼承和虛擬函式來體現的。
編譯時多型:運算子過載上。
虛擬函式: 在基類中用virtual的成員函式。允許在派生類中對基類的虛擬函式重新定義。
基類的虛擬函式可以有函式體,基類也可以例項化。
虛擬函式要有函式體,否則編譯過不去。
虛擬函式在子類中可以不覆蓋。
建構函式不能是虛擬函式。
純虛擬函式:基類中為其派生類保留一個名字,以便派生類根據需要進行定義。
包含一個純虛擬函式的類是抽象類。
純虛擬函式後面有 = 0;
抽象類不可以例項化。但可以定義指標。
如果派生類如果不是先基類的純虛擬函式,則仍然是抽象類。
抽象類可以包含虛擬函式。
5 抽象類和介面的區別
在C++裡面抽象類就是介面
抽象類:定義了純虛擬函式的類是抽象類,不能例項化。
抽象類包括抽象方法(純虛方法),也可以包含普通方法。
抽象類可以派生自一個抽象類,可以覆蓋基類的抽象方法也可以不覆蓋。
雖不能定義抽象類的例項,但是可以定義抽象類的指標。
6 什麼是“引用”?宣告和使用“引用”要注意哪些問題?
引用的特性:
引用是目標變數的別名,對引用的操作與對變數的操作效果一樣。宣告引用的時候要必須對其初始化。引用宣告完後,相當於目標變數有兩個名稱,不能 再把引用作為其他變數的別名。
引用不是新定義一個變數,它只是表示該引用是目標變數名的一個別名,它本身不是一種資料型別,因此引用不佔用儲存單元。
無法建立陣列的引用。因為陣列是一個由若干元素組成的集合,無法建立陣列的別名。
引用的作用:
作為函式的引數,以前用值傳遞,現在用指標或引用。
傳引用和傳指標給函式效果一樣的。
傳遞引用,記憶體中沒有生成實參副本,是直接對實參操作。如果傳遞的是值型別,需要在棧上生成副本,如果是物件,還要呼叫建構函式。
指標呼叫的時候,其實也會形參分配儲存單元,且需要用“指標變數名”的形式運算,容易產生錯誤並且可讀性差;呼叫的時候,需要用變數的地址作為實 參,呼叫形式不好看。引用沒有這些 問題。
引用作為返回值最大的好處是:記憶體中不會產生副本。
但是,引用作為返回值注意事項:
A:不能返回區域性變數的引用。
B:不能返回函式內部new的變數。因為引用僅僅是別名,無法釋放記憶體。
C: 可以返回類成員的引用,但是最好是const
D : 引用和指標一樣,可以產生多型的效果。
總結:
A: 引用的使用主要用於函式傳參,解決大塊資料或物件的問題。
B: 用引用傳遞函式引數,不產生副本,通過const,保證引用傳遞的安全性。
C:比指標的可讀性好,
7 將引用作為函式引數有哪些特點
(1)與指標呼叫效果一樣。
(2)引用傳參,記憶體中並沒有產生副本。
(3)用指標傳遞,也要給形參分配儲存單元;並且需要使用"*變數的"的形式,可讀性差;另外,呼叫的地方還得用地址作為實參。
8 什麼時候用常引用
const int &ra = a; // 不能通過引用對目標變數的值進行修改,從而使引用的目標成為const的,安全。
void bar(String &ra)
bar("AA") // 這個會報錯,因為 ”AA“相當於 const char[], 不能傳遞給bar函式。
可以把函式宣告為Void bar(Const String &ra), 上述語句就不會報錯。
9 引用作為函式返回值型別的格式,好處和規則?
int &fun(int a) {}
好處:不會生成副本。
規則: 不能返回區域性變數的引用;不能返回函式內部new分配的記憶體引用; 如果返回成員的話,返回const'
10 結構與聯合的區別
聯合是公用儲存單元的,任何一個時刻只有一個被選中的成員。一旦賦值後,其他成員也覆蓋了。
11 過載(overload)和重寫(override)?
過載:多個同名函式,引數不同(個數不同,引數型別不同);是同一層級的函式;靜態繫結;編譯期繫結。
重寫:子類重新定義父類函式的方法;是動態繫結。
12 有幾種情況用intialization list(初始化列表)而不是assignment(賦值)?
當類中含有const成員變數; reference成員變數; 基類建構函式需要初始化列表。
13 C++是不是型別按群的?
不是; 兩個不同型別的指標之間可以強制轉換。
14 main函式之前會執行什麼程式碼?
全域性變數的初始化。
15 記憶體分配方式和區別
(1)靜態儲存區:在編譯時就分配好,在整個執行期間都存在。比如全域性變數,static變數。
(2)常量區: 存放常量的,比如字串常量。
(3)堆
(4)棧
16 BOOL,int,float,指標型別,於”零“的比較語句。
BOOL: if(!a) or if(a)
int: if(a == 0)
float: const EXPRESSION exp = 0.000001
if (a < EXP && a > -EXP)
指標: if(a != NULL)
17 const 與 #define相比,優點?
const: 定義常量; 修飾函式引數; 修飾函式返回值; 修飾類成員函式。
好處: const 修飾的有資料型別,而巨集沒有,所以可以做型別檢查;而巨集僅作字元替換,無安全檢查。
const常量可以除錯
巨集有副作用。不加括號的話有副作用。
18 陣列和指標的區別
陣列要麼在靜態儲存區建立,要麼在棧上建立。指標可以隨時指向任意型別的記憶體塊。
char a[] = "khell"; // 棧中分配記憶體,所以可以修改。
a[0] = 'x'; // 可以 沒有問題
char *p = "khell";//常量字串,儲存在字元常量區,不可以修改
p[0] = 'x'; // 編譯可以,執行時錯誤。
sizeof(a) //是陣列的大小;
sizeof(p) // 是指標的大小4.
當陣列作為函式引數進行傳遞時,該陣列退化成指標
void Func(char a[100])
{
cout<< sizeof(a) << endl; // 4 位元組而不是100 位元組
}
陣列名不能自加自減,但是指標可以。
int a[ ] = {1, 2, 3, 4, 5};
int *ptr = (int *) (&a + 1) // a的地址加1後,其實是加了 4*5 = 20那麼多。每次把一個地址加1,都是走資料結構那麼大的步長。
19 int(*s[10])(int)
函式指標陣列,S[10]裡面每個元素都是函式指標,指向函式的型別是 int fun(int a)
void add(int a, int b)
{
cout << a + b << endl;
}
void (*p1)(int a, int b);
p1 = add;
20 為什麼基類的解構函式是虛擬函式?
動態繫結,不會造成潛在的記憶體洩漏
21 全域性變數和區域性變數的區別?如何實現的?作業系統和編譯器是怎麼知道的?
全域性變數分配在全域性資料段(靜態儲存區),在程式開始執行時候載入。區域性變數則分配在堆疊裡面。
22 記憶體分配方式
堆:有記憶體碎片的問題。一定的演算法去找合適的記憶體。效率低。OS有記錄空閒記憶體地址的連結串列
棧:專門的暫存器存放棧地址。效率高。有大小限制。
自由儲存區:用malloc /free分配釋放。 和堆類似。
全域性/靜態儲存區:全域性變數,靜態變數。
常量儲存區:放常量,不允許修改。
int a=0; 全域性/靜態儲存區
char *p1; 全域性/靜態儲存區
int main()
{
int b; //棧
char s[]="abc"; //棧
char *p2; //棧
char *p3="123456"; //123456在常量區,p3在棧上。
static int c =0;//全域性(靜態)初始化區
p1 = (char *)malloc(10); //分配得來得10和20位元組的區域就在堆區
p2 = (char *)malloc(20);
strcpy(p3,"123456"); //123456/0放在常量區,編譯器可能會將它與p3所指向的"123456" 優化成一個地方。
}
23 void *(*(*fp1)(int)[10] / float(*(*fp2)(int, int, int))(int) / int (*(*fp3())[10]()
24 引用與指標區別:
引用必須初始化,指標不用。
引用初始化後不能改變,指標可以改變所指的內容。
不存在指向空值的引用,但是存在指向空值的指標。
指標可以有多級;引用就一級。
指正要解引用,引用不用。
引用沒有const, 但是指標有。
sizeof結果不同。
自增的語義不同。
25 int id[sizeof(unsigned long)] 合法嗎?
可以。陣列的大小在編譯的時候就要確認。
26 棧記憶體與文字常量區域
char str1[] = "abc";
char str2[] = "abc";
const char str3[] = "abc";
const char str4[] = "abc";
const char *str5 = "abc";
const char *str6 = "abc";
char *str7 = "abc";
char *str8 = "abc";
cout << ( str1 == str2 ) << endl;//0 分別指向各自的棧記憶體
cout << ( str3 == str4 ) << endl;//0 分別指向各自的棧記憶體
cout << ( str5 == str6 ) << endl;//1指向文字常量區地址相同
cout << ( str7 == str8 ) << endl;//1指向文字常量區地址相同
結果是:0 0 1 1
解答:str1,str2,str3,str4是陣列變數,它們有各自的記憶體空間;而str5,str6,str7,str8是指標,它們指向相同的常量區域。
27 虛擬函式 VS 純虛擬函式
虛擬函式為了過載和多型,在基類中是有定義的,即便定義為空。在子類中可以重寫。
純虛擬函式在基類中沒有定義,必須在子類中實現。
多型的基礎是繼承,需要虛擬函式的支援。
28 子類不能繼承父類的函式
子類繼承父類大部分的資源,不能繼承的有建構函式,解構函式,拷貝建構函式,operator=函式,友元函式。
29 開發中常用的資料結構:
A:陣列和連結串列:
陣列大小不能動態定義。連結串列和動態分配大小的。
陣列不適應動態增/減的情況,因為大小固定,一旦定義就不能改變。
連結串列適合動態的增加、刪除資料。
陣列的隨機訪問快。
陣列棧中分配; 連結串列在堆中。
B:二叉樹遍歷:
先序、中序、後序。
30 const與static的用法
const:
修飾類成員變數,成員不可以改。
修飾函式引數;
修飾返回值;
修飾函式,函式不會修改類內的資料成員。不會呼叫非const成員函式。(在函式末尾,預設是const this指標,不能修改成員)
const函式只能呼叫const函式,非const函式可以呼叫const函式。
static:
區域性static變數:區域性靜態變數,處於記憶體中的靜態儲存區;只能初始化一次;作用域是區域性。
全域性static變數:全域性靜態變數,靜態儲存區;全域性靜態變數的作用局是宣告它的檔案,在檔案之外是不可見的。其實是從
定義的地方到檔案結尾。
類的static成員:類的全域性變數,被類的所有獨享共享,包括派生類的物件。按照這種方式int base::var = 10;進行
初始化,不能在建構函式內初始化,但是可以用const修飾的static資料成員在類內初始化。
static修飾成員函式,類只有一份,不含this指標。
static成員變數定義放在cpp檔案中。 const static 可以就地初始化。
31 類的static變數在什麼時候初始化,函式的static變數什麼時候初始化?
類的靜態成員在類例項化之前就存在了; 函式的static變數在執行此函式時進行例項化(第一次呼叫的時候,只初始化一次)
32 棧溢位的原因:
棧大小有限制:分過多的陣列;
遞迴呼叫層太深;
33 switch引數型別
可以是:byte short int long bool
不能是: float double(這種浮點型的不能精確的比較,所以不能) string
但是在c++ 11裡面, string可以作為switch的條件了。
35 頻繁出現的短小的函式,在c/C++中分別如何實現
c中用巨集定義; C++ 內聯
36 C++函式傳引數方式
值傳遞、指標、引用
37 定義巨集注意什麼?
定義部分的每個形參和整個表示式都必須用括號括起來。
38 .h標頭檔案中的ifndef/define/endif作用
防止標頭檔案重複包含
39 struct VS class
struct的成員預設是共有的,而類的成員預設是私有的。
繼承的的時候,class預設是私有繼承;結構體是共有繼承;
class還用於定義模板引數,就像typename
40 系統會自動開啟和關閉的三個標準檔案是?
在C語言中,在程式開始執行時,系統自動開啟3個標準檔案:標準輸入、標準輸出、標準出錯輸出。通常這3個檔案都與終端相聯絡。因此,以前我們所用到的從終端輸入或輸出都不需要開啟終端檔案。系統自定義了3個檔案指標 stdin、stdout、stderr,分別指向終端輸入、終端輸出和標準出錯輸出(也從終端輸出)。
標準輸入流:stdin
標準輸出流:stdout
標準錯誤輸出流:stderr
41 如何判斷浮點數是否是0.5
fabs(x - 0.5)< DBL_DEPSILON
42 記憶體洩漏? 指標越界和記憶體洩漏,有哪些方法?
new/delete, new[]/delete, malloc/free 配套使用
對指標賦值的時候,一定要看原來指向的東西是否需要釋放
指標指向的東西釋放後,一定要將指標設定為null。
43 TCP、UDP差別
TCP: 面向連線, 有保障, 效率低, 基於流的,重要資料
udp: 無連線 無保障 效率高 基於資料報文 不重要的資料
44 #include<file.h> #include "file.h"
前者是從標準庫路徑尋找
後者是從當前工作路徑
45 sizeof
sizeof計算的是棧中分配的記憶體大小