1. 程式人生 > >C++ 面試題

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 子類析構時,要呼叫父類的解構函式嗎?

  

    解構函式呼叫的次序時先派生類後基類的。和建構函式的執行順序相反。並且解構函式要是virtual的,否則如果用父類的指標指向子類物件的時候,解構函式靜態繫結,不會呼叫子類的析構。

     不用顯示呼叫,自動呼叫。

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計算的是棧中分配的記憶體大小