C++核心指南(18) I.12-13 不要將一個數組作為指針進行傳遞

C++核心指南(18) I.12-13 不要將一個數組作為指針進行傳遞

I.12: 聲明一個非空指針為not_null

原因

幫助避免nullptr指針的解引用錯誤,通過避免對nullptr的冗餘檢查來提高性能。

示例

 int length(const char* p); // 不清楚length(nullptr)是否合法

length(nullptr); // OK?

int length(not_null<const> p); // better: 我們假定p不能為nullptr

int length(const char* p); // 我們必須假定p可以為nullptr/<const>

通過在源代碼中聲明意圖,實現者和工具可以提供更好的診斷,比如通過靜態分析找到某些錯誤類,以及執行優化,比如刪除分支和空測試。

Note

not_null 定義在 指南支持庫.

Note

指向char的指針指向C風格的字符串(以\\0結尾的字符串)這一個假設也是隱式的,這是困惑和錯誤的一個潛在根源。優先使用czstring,而不是const char*。

 // 我們假設p不能為nullptr
// 我們假設p指向以0結尾的字符數組

int length(not_null<zstring> p);/<zstring>

注意:length()只是std::strlen()的偽裝。

實施

  • (簡單) ((基礎)),如果函數在所有控制流路徑上使用指針參數之前都檢查nullptr,則警告應當聲明為not_null。
  • (複雜) 如果函數在所有返回路徑上都能保證返回的指針不為nullptr,則警告返回類型就聲明為not_null。

I.13: 不要將一個數組作為單個指針進行傳遞

原因

(指針, 大小)風格的接口是易於出錯的,同樣,單獨的指針(指向數組)必須依賴一些慣例才能讓調用者知道它的大小(譯註:數組元素個數)。

示例

考慮:

void copy_n(const T* p, T* q, int n); // 將[p:p+n) 拷貝到 [q:q+n)

如果q指向的數組中少於n個元素怎麼辦?然後,我們就可能覆寫了一些不相關的內存;如果p指向的數組中少於n個元素怎麼辦?然後,我們就可能讀了一些不相關的內存。要麼是一個未定義的行為,要麼就是一個潛在的嚴重bug

可選方法

考慮使用顯示的span:

 void copy(span<const> r, span r2); // copy r to r2/<const>

糟糕的示例

考慮:

 void draw(Shape* p, int n); // 糟糕的接口; 糟糕的代碼
Circle arr[10];
// ...
draw(arr, 10);

傳遞10給參數n可能是錯誤的:最常見的習慣是假設[0:n),但並沒有地方說明這一點。更糟糕的是draw()調用的編譯:有一個隱式從數組到指針的轉換(數組衰退),以及另一個從Circle到Shape的轉換。draw()不能安全地遍歷該數組:無法得知數組元素的數量。

可選方法: 使用一個支持類來確保元素的數量是正確的,並防止危險的隱式轉換,例如:

 void draw2(span<circle>);
Circle arr[10];
// ...
draw2(span<circle>(arr)); // 推導出元素的個數
draw2(arr); // 推導元素類型和數組大小

void draw3(span<shape>);
draw3(arr); // 錯誤:不能將Circle[10] 轉換到 span<shape>/<shape>/<circle>/<circle>

draw2()將同樣的數量信息傳遞給了draw(),它使得該信息就是Circle的範圍這一假定明確起來。

例外

使用zstring和czstring來表示以'\\0'結尾的C風格字符串,當這樣做時,使用 GSL的string_span來阻止錯誤發生。

實施

  • (簡單) ((邊界)) 對所有依賴隱式轉換數組到指針的表達式進行警告,zstring/czstring指針類型例外。
  • (簡單) ((邊界)) 對於指針類型表達式上的任何算術運算,如果運算結果為指針類型值,則發出警告,zstring/czstring指針類型例外。


分享到:


相關文章: