2012年1月16日月曜日

( Qt C++ )コンテナ用イテレータの種類(STLスタイルイテレータ)



はいそれではコンテナ用イテレータの種類について書いていきます。
前回、前々回の記事でQVector,QLindedlist,QListについて書きましたが、そのサンプル内でイテレータを使ったものがあったかと思います。Qtでは
①Javaスタイルイテレータ
②STLスタイルイテレータ
の2種類が用意されています。今回はSTLスタイルイテレータについて書いていきます。(イテレータについてはWikiでも見てください。)


②STLスタイルイテレータ
C<T>::iteratorとC<T>::const_iteratorの2種類がこれに当たります。constなしが読み書きOK、const...のほうが読み込みのみという違いです。このSTLイテレータが指す要素はJavaスタイルイテレータとは違い、要素そのものを指します。以下のような感じです。

STLイテレータはC++のポインタのように使用できます。よってかなり使い易いです。

それではサンプルを書いていきます。(C++ GUI Programming with Qt4 256ページを参考)
いつものようにQtCreaterの使用を前提とします。(QtCreaterなどの使い方は ”Qtをはじめよう" を見てください。)

ではコードを


(C<T>::const_iteratorのサンプル ※読み込みのみ)
QList<QString> list;
list << "One!" << "Two!" << "T";

QList<QString>::const_iterator i = list.begin();

bool T_exist = false;//"T"が含まれているかどうかのフラグ

while(i != list.end())//iが最後のうしろ以外ならループ
{
    if(*i == "T")
    {
        T_exist = true;
        break;
    }
    i++;//次の要素へ
}
//最終的にT_existにはtrueが入ります。

はい簡単ですね。流れはlistのSTLのconstイテレータを取り出し、"T"が含まれているか走査しているだけです。
begin()やend()はSTLスタイルイテレータを返します。(リファレンス)
beginは最初の要素、endは最後の後ろの要素を指します。


(C<T>::iteratorサンプル ※読み書きOK)
QList<QString> list;
list << "One!" << "Two!" << "T";

QList<QString>::iterator i = list.begin();

while(i != list.end())
{
    if(*i == "T")//"T"の値ならば↓
    {
        *i = "Three!";//"Three!"に書き換え
        break;
    }
    i++;
}
//最終的にlistは[ "One!", "Two!", "Three!" ]となる

これも簡単ですね。実際はこちらのほうをよく使うと思います。流れはlistのSTLイテレータを取り出し、"T"が含まれているか走査して、あればその指し示す要素を"Three!"に書き換えているだけです。
(その他の詳細についてはリファレンスを参照してみてください。)


なお、Qtでコンテナクラスを返す関数がありますが、以下のように

//このコードは危険
QList<int>::const_iterator i = splitter->sizes().begin();
while (i != splitter->sizes().end()) {
//...
++i;
}
//このコードは危険

のようアクセスするのはマズイようです。どうもループの先頭でsize()を呼んだ際にリストが自動で消去されイテレータの宙ぶらりんの参照先が残ってしまうからだそうです。しかも悪いことにループを実行するたびsplitter->sizes().end()を呼び出すので、そのループのたびにリストの新しいコピーを生成してしまうことになるそうです。なんだか要素が多いときを考えるとゾッとしますね。

これを避けるには以下のように

QList<int> list = splitter->sizes();//listにコピー
QList<int>::const_iterator i = list.begin();
while (i != list.end()) {
//...
++i;
}
//これは安全

必ず返されたコンテナクラスをコピーしてから反復処理を開始すれば安全だそうです。(C++ GUI Programming with Qt4 257ページに詳細が書かれています。)


以上です。