C++11あなたは本当に反復器(iterator)を使うことができますか?
5134 ワード
C++STLは豊富な標準コンテナ(Container)オブジェクト(vector,array,queue,list,set,unordered_map/set...)を提供し、必要に応じて異なるコンテナを選択して様々なタイプのデータを管理することができます.コンテナの使用といえば、反復器(iterator)を使用しないことは不可能であり、すべてのコンテナオブジェクトは、コンテナの特徴に応じて類似しているが異なるiteratorを提供し、コンテナ内のデータにアクセスするために使用されます.
反復器サイクル
一般的に、1つのコンテナ内のすべてのデータを巡回する場合、プログラマーが最もよく使う書き方は次のとおりです.
範囲ベースforサイクル
C++11は、forループに関する新しい特性を提供します.範囲ベースのforループ(the range-base for statement)、さらに「タイプ導出」特性を加えると、上記のコードをさらに簡略化できます.
違いはありませんか?
明らかに、新しいforループの書き方はもっと簡潔ですが、新しいforループの書き方の長所はそれだけですか?よく考えてみると、最初の書き方は、ループするたびに
これはlistです.end()関数のソースコード(C++11のヘッダファイル
end()関数を呼び出すたびに
では、範囲ベースのforループ(the range-base for statement)は同じ処理方式ではないでしょうか.この問題を検証するために、私の前の文章「C++11カスタムコンテナのために標準を実現するforward反復器」で、カスタムハッシュテーブル(
次に、次のテストコードを実行します.
以下はプログラム出力(debug/release結果同様)
testing for statement using iterator://注、ループで3回end()return a end iterator 2000 return a end iterator 12345 return a end iterator 65535 return a end iterator testing for the range-base for statement://注、ループで1回end()return a end iterator 2000 12345 65535
まとめ
以上の出力は、範囲ベースのforループ(the range-base for statement)がループ開始時に1回だけ
もしあなたがまだ「伝統を堅持する」場合、反復器を直接使用することに慣れている場合は、コードをいくつか改善することをお勧めします.また、最初のコードを例にとると、ループの開始時に
反復器サイクル
一般的に、1つのコンテナ内のすべてのデータを巡回する場合、プログラマーが最もよく使う書き方は次のとおりです.
#include
#include
int main(){
list<int> lst;
for(list<int>::iterator itor=lst.begin();itor!=lst.end();itor++){
cout<//do something
}
}
範囲ベースforサイクル
C++11は、forループに関する新しい特性を提供します.範囲ベースのforループ(the range-base for statement)、さらに「タイプ導出」特性を加えると、上記のコードをさらに簡略化できます.
for(auto &node:lst){
cout<//do something
}
違いはありませんか?
明らかに、新しいforループの書き方はもっと簡潔ですが、新しいforループの書き方の長所はそれだけですか?よく考えてみると、最初の書き方は、ループするたびに
lst.end()
を呼び出すことに気づきます.これはlistです.end()関数のソースコード(C++11のヘッダファイル
stl_list.h
から): /** * Returns a read/write iterator that points one past the last * element in the %list. Iteration is done in ordinary element * order. */
iterator
end() _GLIBCXX_NOEXCEPT
{ return iterator(&this->_M_impl._M_node); }
end()関数を呼び出すたびに
iterator
オブジェクトが返され、反復ループ全体でend()
を呼び出すたびに返されるオブジェクトは実際には完全に同じであり、呼び出されるたびにオブジェクト構造、コピーが発生することは避けられないことがわかります.などの動作は、高性能を要求する応用の場合、このような無意味な繰り返しは受け入れられない.では、範囲ベースのforループ(the range-base for statement)は同じ処理方式ではないでしょうか.この問題を検証するために、私の前の文章「C++11カスタムコンテナのために標準を実現するforward反復器」で、カスタムハッシュテーブル(
HashTableAbstract
)に基づく標準forward反復器を実現しました.そこでHashTableAbstractのend()関数にデバッグコードを追加し、end()
のたびにデバッグ情報を出力しました. iterator end()noexcept
//{ return iterator(this->m_table,this->m_table.capacity); }//
{
cout << "return a end iterator" << endl;//
return iterator(this->m_table, this->m_table.capacity);
}
次に、次のテストコードを実行します.
HashSetCl<int> testhash;//HashSetCl HashTableAbstract ,
testhash.insert(2000);// 3
testhash.insert(65535);
testhash.insert(12345);
cout<<"testing for statement using iterator:"<//
for (auto itor = testhash.begin(); itor != testhash.end(); itor++) {
cout << *itor << endl;
}
cout<<"testing for the range-base for statement:"<// for
for (auto n : testhash) {
cout << n << endl;
}
以下はプログラム出力(debug/release結果同様)
testing for statement using iterator://注、ループで3回end()return a end iterator 2000 return a end iterator 12345 return a end iterator 65535 return a end iterator testing for the range-base for statement://注、ループで1回end()return a end iterator 2000 12345 65535
まとめ
以上の出力は、範囲ベースのforループ(the range-base for statement)がループ開始時に1回だけ
end()
を呼び出すことが確認され、一般的に反復器(iterator)を直接使用するループに比べて、コードが簡潔であるという利点だけでなく、性能にも優れている.もちろんこの結論は無秩序コンテナ反復遍歴(読み取り専用)の場合にのみ有効であり(無秩序コンテナはforward反復器のみを提供する)、ランダムアクセス反復器(random-access iterator)を備えたコンテナ(例えばvector,array)、直接下付きでアクセスするのが最も速い.もしあなたがまだ「伝統を堅持する」場合、反復器を直接使用することに慣れている場合は、コードをいくつか改善することをお勧めします.また、最初のコードを例にとると、ループの開始時に
end()
関数を呼び出して一時変数end
に保存し、ループの比較のたびにend()
関数を呼び出すのではなく、一時変数end
と直接比較します.end()
の繰り返し呼び出しが回避される.for(auto itor=lst.begin(),end=lst.end();itor!=end;itor++){
cout<//do something
}