C++調試之缺陷捕捉

時間:2014.06.25

地點:基地

-----------------------------------------------------------------------------

一、在編譯時捕捉缺陷

  捕捉缺陷可在編譯時發生也可在運行時發生,只要有可能,我們應該盡力在編譯時捕捉缺陷

  原因:

1.在編譯期檢測到的缺陷,給出的是一條文本信息,正確描述了所發生的錯誤是什麼,發生在哪以及發生的位置。

2..完整的編譯完整地覆蓋了程序中的所有代碼,即若編譯器沒有返回錯誤或警告,即可相信程序中百分之百的不存在編譯時可檢測到的錯誤。(這點對於運行時測試 就無法保證了,當代碼龐大時,很難保證所有分支都被測試到,也無法保證程序每行代碼都會至少執行一次,即無法保證代碼測試100%的覆蓋,且即便同一段代碼,對於不同的輸入,測試結果也可能不一樣)

 3.編譯時捕捉到的錯誤可以節省程序維護時間,早發現早超生(運行時錯誤難以複製和跟蹤)

基於這樣一些等等的原因,我們應該儘量在編譯器捕捉到程序缺陷,於是我們在編寫代碼時,應該能使程序具有自我診斷功能,方便在編譯器尋找缺陷。

-----------------------------------------------------------------------------

二、正確處理類型

問題描述

有時,我們編寫一個函數處理一些自定義數據類型,比如我們已經定義好兩個類型:類Apple和類Orange,現有函數聲明如下:

void  DoSomeWithOrange(const Orange& orange);
如果我們在調用DoSomeWithOrange時,不小心傳入了Apple對象,如下:

Apple an_apple(some_inputs);
DoSomeWithOrange(an_apple);
  很多場合下,這樣的誤傳類型的代碼是可以編譯通過的,因爲編譯器總是試圖儘可能地幫你進行類型轉換,使之滿足要求,即Apple可能在這偷偷地轉換爲Orangte類型,我們並不期待這種轉換,於是有必要在編譯器就檢測到這樣的失誤。

  發生這麼偷偷的類型轉換有兩種情況:

a.Orange類具有一個只接受Apple類型參數的構造函數。如下:

class Orange
{
  public:
    Orange(const Apple& apple);
};
或
class Orange
{
  publick:
    Orange(const Apple& apple,const Banana* p_banana=0);
};
像上面這樣的類,在調用需要Orange類的地方,如果誤傳實參Apple類型,Apple類型就會發生這種偷偷的類型轉換。

class Orange
{
  public:
    explicit Orange(const Apple& apple);
};

class Orange
{
  publick:
    explicit Orange(const Apple& apple,const Banana* p_banana=0);
};
這樣,可以防止編譯器執行隱式自動類型轉換,使得在需要期望接受Orange的地方就必須使用Orange了。

b.當然,當提供一個轉換操作符將Apple類型對象顯式轉換爲Orange類型,也會發生這種偷偷的隱式類型轉換

class Apple
{
  public:
    //......
    operator Orange() const;
};

解決辦法是,我們爲Apple類提供了轉換爲Orange的顯式轉換操作符

class Apple
{
  public:
    //......
   Orange AsOrange() const;
};
於是,這樣,我們調用函數時可如下調用:

Apple apple(some_inputs);
DoSomethingWithOrange(apple.AsOrange());  //顯式轉換

-----------------------------------------------------------------------------

三、使用枚舉類型

先定義如下兩個枚舉,分別表一週中的星期和一年的月份

enum{SUM,MON,TUE,WED,THU,FRI,SAT};
enum{JAN=1,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC};
我們知道,這些實際上都是整數,當我們有一個期望接受一週中的星期作爲參數的函數時:

void FuncExpectingDayOfWeek(int day_of_week);
現在,假如我們誤傳了一年中的某個月編譯也將順利通過:

FuncExpectionDayOfWeek(JAN);
在這裏因爲星期和月份的表示所以編譯順利通過,爲了避免在編譯時就捕捉在這種誤傳,我們應該使用創建新類型的枚舉

typedef enum{SUM,MON,TUE,WED,THU,FRI,SAT}DayOfWeek;
enum{JAN=1,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC}Month;
void FuncExpetingDayOfWeek(DayOfWeek day_of_week);
現在,當傳 JAN時將會發生編譯錯誤。

總的一點說來,大多數情況我們應該儘量避免隱式類型轉換,這就允許我們充分利用編譯器檢查不同變量類型的功能,在早期編譯時捕捉到潛在的錯誤。但也總有一些錯誤只能在運行時被檢測得到,那也是不可避免的,所以只是儘量在編譯器檢測到錯誤。

-----------------------------------------------------------------------------

四、總結

  禁止隱式類型轉換:用關鍵字explicit聲明接受1個參數的構造函數,儘量不要使用轉換操作符,而是在類中提供更好的類型轉換方法成員。

不要使用枚舉創建整型常量,而是使用它們創建新類型。










發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章