不是每一個switch語句都需要一個default
為什么需要default
給每一個swith加上default分支,一直是一個推薦的實踐。在以下三種場景下都建議使用default分支。
- 場景一
在switch語句中,前面的case是特殊的處理,default分支里是默認實現。在這種情況下,default分支恰好契合了default的語義。譬如:
void bar(WeekDay day) {
switch (day)
case SATURDAY: {
//something
break;
}
case SUNDAY: {
//something else
break;
}
default: {
//working day, default case
break;
}
}
}
- 場景二
利用default分支來捕獲設計中沒有考慮到的異常的值,譬如:
void foo(int type) {
switch(type) {
case 1: {
//something
break;
}
case 2: {
//something else
break;
}
default: {
// unknown type!
// error-handling
break;
}
}
}
- 場景三
default分支里不需要做任何處理。default在這里提醒讀者,這種情況已經考慮到了,只是沒有什么語句需要執行。
switch(keystroke) {
case 'w': {
// move up
break;
}
case 'a': {
// move left
break;
}
case 's': {
// move down
break;
}
case 'd': {
// move right
break;
}
default: {
//nothing
break;
}
}
default分支引入dead code
而在某些場景下case分支已經窮舉了所有的可能,default分支引入的是一段永遠不會被運行到的死代碼。每一行代碼讀增加了閱讀、維護的成本,良好的編程實踐一直要求我們盡量刪除死代碼。這種場景下default分支是否應該刪除呢?考量下面兩個例子:
例1
int flag = value > 1000 ? 1 : 0;
switch(flag) {
case 0: {
//something
break;
}
case 1: {
//something else
break;
}
default: {
//for sanity, never reach here
break;
}
}
可以很容易地發現default分支永遠不會被執行。default分支的唯一作用類似于注釋,表示前面的case確實已經窮舉了所有的可能。在這種情況下是否定義default分支,取決于強迫癥的嚴重程度,加或是不加沒有太大分別,關鍵在于制訂統一的規則然后嚴格地執行。
例 2
enum Colour {
RED,
WHITE
};
void test(const Colour& colour) {
switch(colour) {
case RED: {
//something
break;
}
case WHITE: {
//something else
break;
}
default: {
//nothing
break;
}
}
}
這個例子里,default分支里邏輯是不是死代碼呢?目前看是的,但當某一天Colour的定義發生了變化增加了其他定義,default里的邏輯就不再是死代碼。
這個例子可以歸為場景2,利用default分支來捕獲設計中沒有考慮到的異常的值。在default分支中增加錯誤處理邏輯,一旦進入該分支就說明出現了意料之外的值,需要對程序做修復。
不需要default的場景
至此,我們都在討論為什么需要default,那么什么時候不需要default呢?參考某司的編程規范:
Avoid default case (but set a default value before switch/case) for switch/case on all values of an enum (N/A if not on all values) to detect missing cases by compilation.
這條規范要表達的意思是,如果switch里的變量類型是枚舉類型,不要加default分支。如果漏掉了針對某個枚舉值的分支,在沒有default分支的情況下,會有編譯告警。
測試結果
在mac os + clang
及Linux + gcc
的環境下,如果定義了default分支,編譯通過。如果不定義default分支,會產生編譯告警。
結論
在使用枚舉值作為switch的參數時,如果漏掉了對某個值的處理,不加default 分支時會產生編譯告警,可以快速發現潛在的問題。而定義default分支并在default分支中添加錯誤處理的方式,需要在運行時才有可能發現錯誤。只有當用漏掉處理的值作為參數來觸發switch邏輯時問題才會暴露。
基于此,建議在使用枚舉值作為switch的參數,并且需要窮舉所有枚舉值時不要定義default分支。