1. 程式人生 > >C++面試前必須複習的知識點

C++面試前必須複習的知識點

1. 用變數a給出下面的定義

a) 一個整型數(An integer)

b) 一個指向整型數的指標(A pointer to an integer)

c) 一個指向指標的的指標,它指向的指標是指向一個整型數(A pointer to a pointer to an integer)

d) 一個有10個整型數的陣列(Anarray of 10 integers)

e) 一個有10個指標的陣列,該指標是指向一個整型數的(An array of 10 pointers to integers)

f) 一個指向有10個整型數陣列的指標(Apointer to an array of 10 integers)

g) 一個指向函式的指標,該函式有一個整型引數並返回一個整型數(A pointer to a function that takes an integer as an argument andreturns an integer)

h) 一個有10個指標的陣列,該指標指向一個函式,該函式有一個整型引數並返回一個整型數( An array of ten pointers to functions that take an integer argumentand return an integer )

答案是:

a) int a; // An integer

b) int *a; // A pointer to an integer

c) int **a; // A pointer to a pointer to aninteger

d) int a[10]; // An array of 10 integers

e) int *a[10]; // An array of 10 pointersto integers

f) int (*a)[10]; // A pointer to an arrayof 10 integers

g) int (*a)(int); // A pointer to afunction a that takes an integer argument and returns an integer

h) int (*a[10])(int);

// An array of 10 pointers tofunctions that take an integer argument and return an integer 

2.static關鍵字的作用是什麼?

1). 在函式體,一個被宣告為靜態的變數在這一函式被呼叫過程中維持其值不變。

2).在模組內(但在函式體外),一個被宣告為靜態的變數可以被模組內所用函式訪問,但不能被模組外其它函式訪問。它是一個本地的全域性變數。

3).在模組內,一個被宣告為靜態的函式只可被這一模組內的其它函式呼叫。那就是,這個函式被限制在宣告它的模組的本地範圍內使用。

3.關鍵字const的含義

(1)const意味著只讀;

(2)

Const int a;     intconst a;     const int *a;     int * const a;     int const * a const;

前兩個的作用是一樣,a是一個常整型數。第三個意味著a是一個指向常整型數的指標(也就是,整型數是不可修改的,但指標可以)。第四個意思a是一個指向整型數的常指標(也就是說,指標指向的整型數是可以修改的,但指標是不可修改的)。

4.

關鍵字volatile有什麼含意 並給出三個不同的例子。

一個定義為volatile的變數是說這變數可能會被意想不到地改變,這樣,編譯器就不會去假設這個變數的值了。精確地說就是,優化器在用到這個變數時必須每次都小心地重新讀取這個變數的值,而不是使用儲存在暫存器裡的備份。下面是volatile變數的幾個例子:

1). 並行裝置的硬體暫存器(如:狀態暫存器)

2). 一箇中斷服務子程式中會訪問到的非自動變數(Non-automatic variables)

3). 多執行緒應用中被幾個任務共享的變數

假設被面試者正確地回答了這是問題(嗯,懷疑這否會是這樣),我將稍微深究一下,看一下這傢伙是不是直正懂得volatile完全的重要性。

1). 一個引數既可以是const還可以是volatile嗎?解釋為什麼。

2). 一個指標可以是volatile 嗎?解釋為什麼。

3). 下面的函式有什麼錯誤:

int square(volatile int*ptr)

{

return *ptr * *ptr;

}

下面是答案:

1). 是的。一個例子是隻讀的狀態暫存器。它是volatile因為它可能被意想不到地改變。它是const因為程式不應該試圖去修改它。

2). 是的。儘管這並不很常見。一個例子是當一箇中服務子程式修該一個指向一個buffer的指標時。

3). 這段程式碼的有個惡作劇。這段程式碼的目的是用來返指標*ptr指向值的平方,但是,由於*ptr指向一個volatile型引數,編譯器將產生類似下面的程式碼:

int square(volatile int *ptr)

{

int a,b;

a = *ptr;

b = *ptr;

return a * b;

}

由於*ptr的值可能被意想不到地該變,因此a和b可能是不同的。結果,這段程式碼可能返不是你所期望的平方值!正確的程式碼如下:

long square(volatile int *ptr)

{

int a;

a = *ptr;

return a * a;

}

5.

嵌入式系統經常具有要求程式設計師去訪問某特定的記憶體位置的特點。在某工程中,要求設定一絕對地址為0x67a9的整型變數的值為0xaa66。編譯器是一個純粹的ANSI編譯器。寫程式碼去完成這一任務。

這一問題測試你是否知道為了訪問一絕對地址把一個整型數強制轉換(typecast)為一指標是合法的。這一問題的實現方式隨著個人風格不同而不同。典型的類似程式碼如下:

int *ptr;

ptr = (int *)0x67a9;

*ptr = 0xaa55;

6. 下面的程式碼輸出是什麼,為什麼?

Void foo(void)

{

unsigned int a= 6;

int b = -20;

(a+b > 6) ?puts("> 6") :puts("<= 6");

}

這個問題測試你是否懂得C語言中的整數自動轉換原則,我發現有些開發者懂得極少這些東西。不管如何,這無符號整型問題的答案是輸出是“>6”。原因是當表示式中存在有符號型別和無符號型別時所有的運算元都自動轉換為無符號型別。 因此-20變成了一個非常大的正整數,所以該表示式計算出的結果大於6。這一點對於應當頻繁用到無符號資料型別的嵌入式系統來說是豐常重要的。如果你答錯了這個問題,你也就到了得不到這份工作的邊緣。

7.

 什麼是“引用”?申明和使用“引用”要注意哪些問題?

答:引用就是某個目標變數的“別名”(alias),對引用的操作與對變數直接操作效果完全相同。申明一個引用的時候,切記要對其進行初始化。引用宣告完畢後,相當於目標變數名有兩個名稱,即該目標原名稱和引用名,不能再把該引用名作為其他變數名的別名。宣告一個引用,不是新定義了一個變數,它只表示該引用名是目標變數名的一個別名,它本身不是一種資料型別,因此引用本身不佔儲存單元,系統也不給引用分配儲存單元。不能建立陣列的引用。

8.將“引用”作為函式引數有哪些特點?

1傳遞引用給函式與傳遞指標的效果是一樣的。這時,被調函式的形參就成為原來主調函式中的實參變數或物件的一個別名來使用,所以在被調函式中對形參變數的操作就是對其相應的目標物件(在主調函式中)的操作。

2使用引用傳遞函式的引數,在記憶體中並沒有產生實參的副本,它是直接對實參操作;而使用一般變數傳遞函式的引數,當發生函式呼叫時,需要給形參分配儲存單元,形參變數是實參變數的副本;如果傳遞的是物件,還將呼叫拷貝建構函式。因此,當引數傳遞的資料較大時,用引用比用一般變數傳遞引數的效率和所佔空間都好。

3使用指標作為函式的引數雖然也能達到與使用引用的效果,但是,在被調函式中同樣要給形參分配儲存單元,且需要重複使用"*指標變數名"的形式進行運算,這很容易產生錯誤且程式的閱讀性較差;另一方面,在主調函式的呼叫點處,必須用變數的地址作為實參。而引用更容易使用,更清晰。

引用型引數應該在能被定義為const的情況下,儘量定義為const

9.將“引用”作為函式返回值型別的格式、好處和需要遵守的規則?

格式:型別識別符號 &函式名(形參列表及型別說明){ //函式體 }

好處:在記憶體中不產生被返回值的副本;(注意:正是因為這點原因,所以返回一個區域性變數的引用是不可取的。因為隨著該區域性變數生存期的結束,相應的引用也會失效,產生runtime error!

注意事項:

(1)不能返回區域性變數的引用。這條可以參照Effective C++[1]的Item 31。主要原因是區域性變數會在函式返回後被銷燬,因此被返回的引用就成為了"無所指"的引用,程式會進入未知狀態。

(2)不能返回函式內部new分配的記憶體的引用。這條可以參照Effective C++[1]的Item 31。雖然不存在區域性變數的被動銷燬問題,可對於這種情況(返回函式內部new分配記憶體的引用),又面臨其它尷尬局面。例如,被函式返回的引用只是作為一個臨時變量出現,而沒有被賦予一個實際的變數,那麼這個引用所指向的空間(由new分配)就無法釋放,造成memoryleak。

(3)可以返回類成員的引用,但最好是const。這條原則可以參照EffectiveC++[1]的Item 30。主要原因是當物件的屬性是與某種業務規則(business rule)相關聯的時候,其賦值常常與某些其它屬性或者物件的狀態有關,因此有必要將賦值操作封裝在一個業務規則當中。如果其它物件可以獲得該屬性的非常量引用(或指標),那麼對該屬性的單純賦值就會破壞業務規則的完整性。

(4)流操作符過載返回值申明為“引用”的作用:流操作符<<和>>,這兩個操作符常常希望被連續使用,例如:cout << "hello" << endl; 因此這兩個操作符的返回值應該是一個仍然支援這兩個操作符的流引用。可選的其它方案包括:返回一個流物件和返回一個流物件指標。但是對於返回一個流物件,程式必須重新(拷貝)構造一個新的流物件,也就是說,連續的兩個<<操作符實際上是針對不同物件的!這無法讓人接受。對於返回一個流指標則不能連續使用<<操作符。因此,返回一個流物件引用是惟一選擇。這個唯一選擇很關鍵,它說明了引用的重要性以及無可替代性,也許這就是C++語言中引入引用這個概念的原因吧。賦值操作符=。這個操作符象流操作符一樣,是可以連續使用的,例如:x = j = 10;或者(x=10)=100;賦值操作符的返回值必須是一個左值,以便可以被繼續賦值。因此引用成了這個操作符的惟一返回值選擇。

例3

#include <iostream.h>

int &put(int n);

int vals[10];

int error=-1;

void main()

{

put(0)=10; //以put(0)函式值作為左值,等價於vals[0]=10;

put(9)=20; //以put(9)函式值作為左值,等價於vals[9]=20;

cout<<vals[0];

cout<<vals[9];

}

int &put(int n)

{

if (n>=0&& n<=9 ) return vals[n];

else {cout<<"subscript error"; return error; }

}

(5)在另外的一些操作符中,卻千萬不能返回引用:+-*/ 四則運算子。它們不能返回引用,Effective C++[1]的Item23詳細的討論了這個問題。主要原因是這四個操作符沒有side effect,因此,它們必須構造一個物件作為返回值,可選的方案包括:返回一個物件、返回一個區域性變數的引用,返回一個new分配的物件的引用、返回一個靜態物件引用。根據前面提到的引用作為返回值的三個規則,第2、3兩個方案都被否決了。靜態物件的引用又因為((a+b) == (c+d))會永遠為true而導致錯誤。所以可選的只剩下返回一個物件了。

10. “引用”與多型的關係?

引用是除指標外另一個可以產生多型效果的手段。這意味著,一個基類的引用可以指向它的派生類例項。

11.“引用”與指標的區別是什麼?
指標通過某個指標變數指向一個物件後,對它所指向的變數間接操作。程式中使用指標,程式的可讀性差;而引用本身就是目標變數的別名,對引用的操作就是對目標變數的操作。

12.什麼時候需要“引用”?

流操作符<<和>>、賦值操作符=的返回值、拷貝建構函式的引數、賦值操作符=的引數、其它情況都推薦使用引用。

13,。.面向物件的三個基本特徵,並簡單敘述之?

1. 封裝:將客觀事物抽象成類,每個類對自身的資料和方法實行protection(private, protected,public)

2. 繼承:廣義的繼承有三種實現形式:實現繼承(指使用基類的屬性和方法而無需額外編碼的能力)、可視繼承(子窗體使用父窗體的外觀和實現程式碼)、介面繼承(僅使用屬性和方法,實現滯後到子類實現)。前兩種(類繼承)和後一種(物件組合=>介面繼承以及純虛擬函式)構成了功能複用的兩種方式。

3. 多型:是將父物件設定成為和一個或更多的他的子物件相等的技術,賦值之後,父物件就可以根據當前賦值給它的子物件的特性以不同的方式運作。簡單的說,就是一句話:允許將子類型別的指標賦值給父類型別的指標。

14.多型的作用?

主要是兩個:1. 隱藏實現細節,使得程式碼能夠模組化;擴充套件程式碼模組,實現程式碼重用;2. 介面重用:為了類在繼承和派生的時候,保證使用家族中任一類的例項的某一屬性時的正確呼叫。

15.main 函式執行以前,還會執行什麼程式碼?

答案:全域性物件的建構函式會在main函式之前執行。

16. 描述記憶體分配方式以及它們的區別?

1)從靜態儲存區域分配。記憶體在程式編譯的時候就已經分配好,這塊記憶體在程式的整個執行期間都存在。例如全域性變數,static 變數。

2)在棧上建立。在執行函式時,函式內區域性變數的儲存單元都可以在棧上建立,函式執行結束時這些儲存單元自動被釋放。棧記憶體分配運算內置於處理器的指令集。

3)從堆上分配,亦稱動態記憶體分配。程式在執行的時候用malloc 或new 申請任意多少的記憶體,程式設計師自己負責在何時用free 或delete 釋放記憶體。動態記憶體的生存期由程式設計師決定,使用非常靈活,但問題也最多。

17.請說出const與#define 相比,有何優點?

答案:1) const 常量有資料型別,而巨集常量沒有資料型別。編譯器可以對前者進行型別安全檢查。而對後者只進行字元替換,沒有型別安全檢查,並且在字元替換可能會產生意料不到的錯誤。

     2) 有些整合化的除錯工具可以對const 常量進行除錯,但是不能對巨集常量進行除錯。

18.類成員函式的過載、覆蓋和隱藏區別?

答案:

a.成員函式被過載的特徵:

(1)相同的範圍(在同一個類中);

(2)函式名字相同;

(3)引數不同;

(4)virtual 關鍵字可有可無。

b.覆蓋是指派生類函式覆蓋基類函式,特徵是:

(1)不同的範圍(分別位於派生類與基類);

(2)函式名字相同;

(3)引數相同;

(4)基類函式必須有virtual 關鍵字。

c.“隱藏”是指派生類的函式遮蔽了與其同名的基類函式,規則如下:

(1)如果派生類的函式與基類的函式同名,但是引數不同。此時,不論有無virtual關鍵字,基類的函式將被隱藏(注意別與過載混淆)。

(2)如果派生類的函式與基類的函式同名,並且引數也相同,但是基類函式沒有virtual關鍵字。此時,基類的函式被隱藏(注意別與覆蓋混淆)

19. 如何打印出當前原始檔的檔名以及原始檔的當前行號?

答案:

cout << __FILE__ ;

cout<<__LINE__ ;

__FILE__和__LINE__是系統預定義巨集,這種巨集並不是在某個檔案中定義的,而是由編譯器定義的。

20. 如何判斷一段程式是由C 編譯程式還是由C++編譯程式編譯的?

答案:

#ifdef __cplusplus

cout<<"c++";

#else

cout<<"c";

#endif