簡體   English   中英

使用 STL 容器時應該使用 int 還是 unsigned int?

[英]Should I use int or unsigned int when working with STL container?

參考本指南:
https://google.github.io/styleguide/cppguide.html#Integer_Types

Google 建議在大多數情況下使用int
我嘗試遵循本指南,唯一的問題是 STL 容器。


示例 1。

void setElement(int index, int value)
{
    if (index > someExternalVector.size()) return;
    ...
}

比較index.size()會產生警告。

示例 2。

for (int i = 0; i < someExternalVector.size(); ++i)
{
    ...
}

i.size()之間的警告相同。


如果我將indexi聲明為unsigned int ,則警告關閉,但類型聲明會傳播,然后我必須將更多變量聲明為unsigned int ,然后它與指南相矛盾並失去一致性。

我能想到的最好方法是使用演員表:

if (index > static_cast<int>(someExternalVector.size())

要么

for (int i = 0; i < static_cast<int>(someExternalVector.size()); ++i)

但我真的不喜歡演員陣容。

有什么建議嗎?


下面是一些詳細的想法:

僅使用有符號整數的好處是:我可以避免有符號/無符號警告、強制轉換,並確保每個值都可以是負數(以保持一致),因此 -1 可用於表示無效值。

在很多情況下,循環計數器的使用與一些其他常量或結構成員混合使用。 因此,如果簽名/未簽名不一致,則會出現問題。 會有很多警告和鑄件。

無符號類型具有三個特征,其中一個是定性的“好”,另一個是定性的“壞”:

  • 它們可以容納兩倍於相同大小的有符號類型的值(好)
  • size_t版本(即 32 位機器上的 32 位,64 位機器上的 64 位等)對於表示內存(地址、大小等)(中性)很有用
  • 它們在 0 以下換行,因此在循環中減 1 或使用 -1 表示無效索引可能會導致錯誤(壞)。有符號類型也會換行

由於上述前兩點,STL 使用無符號類型:為了不限制類似數組的類的潛在大小,例如vectordeque (盡管您必須質疑在數據結構中需要 4294967296 個元素的頻率); 因為負值永遠不會成為大多數數據結構的有效索引; 並且因為size_t是用於表示與內存有關的任何事情的正確類型,例如結構的大小以及相關的事情,例如字符串的長度(見下文)。這不一定是使用它的好理由索引或其他非內存用途,例如循環變量。 在 C++ 中這樣做的最佳實踐的原因是一種反向構造,因為它是在容器以及其他方法中使用的,一旦使用,其余代碼必須匹配以避免遇到同樣的問題。

當值可能變為負數時,您應該使用有符號類型。

當值不能變為負數時,您應該使用無符號類型(可能與“不應該”不同。)

在處理內存大小sizeof的結果,通常是字符串長度等)時,您應該使用size_t 。它通常被選為默認的無符號類型來使用,因為它與代碼編譯的平​​台相匹配。 例如,字符串的長度是size_t因為一個字符串只能有 0 個或多個元素,並且沒有理由限制字符串的長度方法任意短於平台上可以表示的長度,例如 16 位32 位平台上的長度 (0-65535)。 注意(感謝評論者Morwenstd::intptr_tstd::uintptr_t在概念上相似 - 總是適合您的平台的大小 - 如果您想要不是指針的東西,應該用於內存地址。 注意 2(感謝評論者rubenvb ),由於npos的值,字符串只能容納size_t-1元素。 詳情如下。

這意味着如果使用 -1 表示無效值,則應使用有符號整數。 如果您使用循環向后迭代您的數據,並且您不確定循環構造是否正確,則應考慮使用有符號整數(並且如其他答案之一所述,它們很容易出錯。)IMO ,您不應該使用技巧來確保代碼正常工作 - 如果代碼需要技巧,這通常是一個危險信號。 此外,關注您並閱讀您的代碼的人將更難以理解。 這兩個都是不遵循@Jasmin Gray 上面的回答的原因。

迭代器

但是,使用基於整數的循環來迭代數據結構的內容是在 C++ 中執行此操作的錯誤方法,因此從某種意義上說,關於有符號與無符號 for 循環的爭論是沒有實際意義的。 您應該改用迭代器:

std::vector<foo> bar;
for (std::vector<foo>::const_iterator it = bar.begin(); it != bar.end(); ++it) {
  // Access using *it or it->, e.g.:
  const foo & a = *it;

執行此操作時,您無需擔心類型轉換、簽名等問題。

迭代器可以向前(如上)或反向,用於向后迭代。 使用與it != bar.end()相同的語法,因為end()表示迭代結束,而不是底層概念數組、樹或其他結構的結束。

換句話說,您的問題“在使用 STL 容器時我應該使用 int 還是 unsigned int?”的答案。 是'都不是。 改用迭代器。 閱讀更多關於:

還剩下什么?

如果循環不使用整數類型,還剩下什么? 您自己的值取決於您的數據,但在您的情況下包括使用 -1 表示無效值。 這很簡單。 使用簽名。 只要保持一致。

我非常相信使用自然類型,例如枚舉,並且有符號整數適合於此。 它們更符合我們的概念預期。 當你的思想和代碼一致時,你就不太可能寫出有缺陷的代碼,而更有可能寫出正確、干凈的代碼。

使用容器返回的類型。 在這種情況下, size_t - 這是一個無符號整數類型。 (從技術上講,它是std::vector<MyType>::size_type ,但通常定義為 size_t,因此使用 size_t 是安全的。無符號也可以)

但總的來說,為正確的工作使用正確的工具。 “指數”是否應該為負數? 如果沒有,請不要簽署。

順便說一句,您不必鍵入“unsigned int”。 'unsigned' 是相同變量類型的簡寫:

int myVar1;
unsigned myVar2;

原始問題中鏈接的頁面說:

有些人,包括一些教科書作者,建議使用無符號類型來表示從不為負的數字。 這是一種自我記錄的形式。 但是,在 C 中,此類文檔的優勢被它可能引入的實際錯誤所抵消。

這不僅僅是自我記錄,而是為正確的工作使用正確的工具。 說“無符號變量會導致錯誤,所以不要使用無符號變量”是愚蠢的。 有符號變量也可能導致錯誤。 浮點數也可以(不僅僅是整數)。 唯一保證無錯誤的代碼是不存在的代碼。

他們為什么無符號是邪惡的例子是這個循環:

for (unsigned int i = foo.Length()-1; i >= 0; --i)

我很難在循環中向后迭代,而且我通常會犯錯誤(有符號或無符號整數)。 我要從尺寸中減去一個嗎? 我是讓它大於與等於 0,還是只是大於? 這是一個草率的情況開始。

那么你如何處理你知道有問題的代碼呢? 你改變你的編碼風格來解決問題,讓它更簡單,更容易閱讀,更容易記住。 他們發布的循環中有一個錯誤。 錯誤是,他們希望允許低於零的值,但他們選擇使其無符號。 他們的錯誤。

但這里有一個簡單的技巧,可以讓閱讀、記憶、編寫和運行變得更容易。 帶有無符號變量。 這是明智的做法(顯然,這是我的意見)。

for(unsigned i = myContainer.size(); i--> 0; )
{
    std::cout << myContainer[i] << std::endl;
}

它是未簽名的。 它總是有效。 對起始大小沒有負面影響。 不用擔心下溢。 它只是有效。 這很聰明。 做對了,不要停止使用無符號變量,因為某處有人曾經說過他們在 for() 循環中犯了錯誤並且未能訓練自己不犯錯誤

記住它的技巧:

  1. 將“i”設置為大小。 (不要擔心減去一個)
  2. 使 'i' 像箭頭一樣指向0。 i --> 0 (它是后遞減 (i--) 和大於比較 (i > 0) 的組合)

最好先教自己正確編碼的技巧,然后因為你沒有正確編碼而扔掉工具。

您希望在代碼中看到哪些內容?

for(unsigned i = myContainer.size()-1; i >= 0; --i)

要么:

for(unsigned i = myContainer.size(); i--> 0; )

不是因為要輸入的字符少(這很愚蠢),而是因為它不那么精神混亂。 在瀏覽代碼時進行心理解析更簡單,也更容易發現錯誤。


自己試試代碼

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM