天天看點

Nowcoder專項練習:C++(十五)

1,構造、析構與異常

對于構造函數而言:

  • 不建議在構造函數中跑出異常;
  • 構造函數抛出異常時,析構函數将不會執行,需要手動去釋放記憶體。

對于析構函數而言:

  • 析構函數不應該抛出異常;
  • 當析構函數中會有一些可能發生異常時,那麼就必須要把這種可能發生的異常完全封裝在析構函數内部,決不能讓它抛出函數之外;
  • 析構函數異常相對要複雜一些,存在一種沖突狀态,程式将直接崩潰:異常的被稱為“棧展開(stack unwinding)”的過程中時,從析構函數抛出異常,C++運作時系統會處于無法決斷的境遇,是以C++語言擔保,當處于這一點時,會調用 terminate()來殺死程序。是以,當處理另一個異常的過程中時,不要從析構函數抛出異常, 抛出異常時,其子對象将被逆序析構。

2,指針與數組

Q:

main()
{
    char*a[]={"work","at","alibaba"};
    char**pa=a;
    pa++;
    printf("%s",*pa);
}
           

上述程式的執行結果是?

A:

首先,對于編譯器而言,是沒有數組這一概念的,數組全部都被看做指針, 是以,a[]就是*a,那麼a換成了pa,pa即是a,隻是一個名字的變化而已。根據pa++,也就是取a[1][]的值,也就是"at"

Nowcoder專項練習:C++(十五)

3,scanf與printf

scanf和printf對資料長度的控制不同:

  • scanf不能控制精度隻能控制長度,比如%m.nf是不允許的的,但是可以指定%mf,其中m為整數。
  • printf可以控制長度也可以控制精度。

4,二維數組的定義

定義二維數組,行數可以省略,但是列數一定需要指定,因為編譯器會根據列數來進行尋址。

多元數組也是一樣,隻有最靠近數組名的那一維大小可以省略。

5,類的隐式轉換

  • 當A是基類,B是派生類,則此時B可以隐式轉換為A,因為向上級類型轉換時隐式的,部分元素丢棄可以自動完成。但是,A不能隐式地轉換為B,因為向下是顯式的,不知道添加的值什麼。

6,switch

switch語句中,如果不加break,則成功判斷成功之後的語句會忽略之後的case條件判斷,一直執行下去,直到break語句或者case語句。

換句話說,switch語句中沒有break會執行滿足條件的和之後的所有條件。

7,虛函數與内聯

虛函數不可以内聯,因為虛函數是在運作期間的時候确定調用的函數,而内聯函數是在編譯期間進行代碼展開,兩者沖突,是以沒有一起使用的做法。

當然,内聯隻是對編譯器的一種請求,是否真正内聯是要看編譯器的處理,虛函數可以聲明為内聯,但是編譯器一定不會響應内聯的請求。

8,字元數組的輸入

Q:

定義語句:

int b;
char c[10];
           

則正确的輸入語句為?

A:

  • scanf("%d%s", &b, &c);
  • scanf("%d%s", &b, &c);

對于一個在棧上配置設定的數組,且在建立的代碼塊中進行通路的化,“c”實際上有兩種含義:

  1. 一個指向十個char類型元素的數組;
  2. 一個char* 類型的指針。

對于第一種而言,對其進行sizeof©操作,得到的結果為10。

那麼,何時是第一種,何時是第二種呢?

實際上,第二種是第一種的一種文法糖,是語言設計這為了友善而下放的一個空子。這種文法糖,在二維數組及以上就不成立了。

這就是所謂的上下文語義(編譯器的語義)。

  • scanf("%s", c); //這裡c是一個char* 類型的指針,編譯器相信程式員将它指向了一塊記憶體塊;
  • scanf("%s", &c); //這裡c是一個指向十個char元素的數組的指針,這種才是最正統的用法。

9,宏定義與文法檢查

宏定義不做文法檢查。

預處理是在編譯之前的處理,而編譯的工作之一就是文法檢查,是以預處理并不做文法檢查。

隻有當調用這個宏定義時才會去檢查定義是否正确。

10,拷貝構造函數的調用

拷貝構造函數的調用時機:

  1. 當用類的一個對象初始化類的另一個對象時;
  2. 如果函數的形參是類的對象,調用函數時,進行形參和實參的結合;
  3. 如果函數的傳回值是類的對象,函數執行完成傳回調用者時;
  4. 需要産生一個臨時類對象時;