C++核心指南(12) I.4: 使接口精確且強類型

C++核心指南(12) I.4: 使接口精確且強類型

I.4: 使接口精確且強類型

Reason

類型是最簡單和最好的文檔,其明確的含義提高了可讀性,亦可在編譯時進行檢查。此外,精確類型的代碼通常優化得更好。

示例, 不要這樣用

Consider:

 void pass(void* data); // 弱類型和未限定的void*是可疑的

調用者不確定什麼類型是允許的,由於const沒有指定,所以也不確定data是否可變。注意,所有的指針類型都可以被隱藏轉換成void*,所以調用者很容易提供這個值(譯註:太過隨意)。

被調用者必須要static_cast到未確認的類型來使用data(譯註:通常不能直接操作void*類型),這是易於出錯且囉唆的。

在C++中僅僅使用const void*來傳遞數據也是難以捉摸的,考慮使用variant或者指針基類的指針。

可先方案: 通常,模板參數可以通過將其轉換成T*或T&來消除void*,在泛型代碼中T可以通用或概念受限的模板參數。

糟糕的示例

考慮:

 draw_rect(100, 200, 100, 500); // 這些數字指什麼?

draw_rect(p.x, p.y, 10, 20); // 10和20的單位是什麼?

很明顯,調用者正在描述一個矩形,但它們關聯的部分並不明顯,並且int可以攜帶任意形式的信息,包括許多單位的值,所以我們需要猜測4個int的含義,前面兩個很有可能是x和y座標,但後面兩個是什麼呢?

雖然註釋和參數名字是有幫助的,但是我們可以顯示地這樣聲明:

 void draw_rectangle(Point top_left, Point bottom_right);
void draw_rectangle(Point top_left, Size height_width);

draw_rectangle(p, Point{10, 20}); // 兩個角落(譯註:左上,右下)
draw_rectangle(p, Size{10, 20}); // 一個角落和一個(height, width)對

顯然,我們無法使用靜態類型系統來捕獲所有的錯誤(例如,按照慣例(名稱和註釋)左上角應是第一個參數)。

糟糕的例子

考慮如下:

 set_settings(true, false, 42); //數字指什麼?

參數類型及其值沒有表達出正在指定的設置或這些值的含義。

(下面)這個設計更加顯示、安全且易讀:

 alarm_settings s{};
s.enabled = true;
s.displayMode = alarm_settings::mode::spinning_light;
s.frequency = alarm_settings::every_10_seconds;
set_settings(s);

考慮使用枚舉(enum)來表示一組布爾值(boolean),這是表示一組布爾值的模式。

nable_lamp_options(lamp_option::on | lamp_option::animate_state_transitions);

糟糕的示例

這下面這個示例中,time_to_blink的含義不併能直觀地從接口中看出,秒?毫秒?

 void blink_led(int time_to_blink) // 差 -- 單位是有歧義的
{
// ...
// 使用time_to_blink做一些事
// ...
}

void use()
{

blink_led(2);
}

好的示例

std::chrono::duration(C++11)有助於顯示錶達連續時間的單位。

 void blink_led(milliseconds time_to_blink) // 好 -- 單位明確
{
// ...
// 使用time_to_blink做一些事
// ...
}

void use()
{
blink_led(1500ms);
}

該函數也可以這樣寫來接收任意的時間單位:

 template
void blink_led(duration time_to_blink) // 好 -- 接收任意單位
{
// 假定毫秒是相關的最小單位
auto milliseconds_to_blink = duration_cast(time_to_blink);
// ...
// 使用milliseconds_to_blink來做事
// ...
}

void use()
{
blink_led(2s);
blink_led(1500ms);
}

強制執行

  • (簡單) 報告所有使用void*作為參數或返回值的函數。
  • (簡單) 報告不止一個bool類型參數的函數。
  • (很難做到很好) 尋找使用太多基本類型參數的函數


分享到:


相關文章: