2

http://developer-resource.blogspot.com.au/2009/01/pros-and-cons-of-returing-references.htmlで見つけたブログは次のように書いています。

このコードベースでしばらく作業した後、参照を返すことは悪であり、ポインタを返すのと同じように扱う必要があると思います。これは避けてください。

たとえば、デバッグに1週間かかった問題は、次のとおりです。

class Foo {
        std::vector< Bar > m_vec;
      public:
        void insert(Bar& b) { m_vec.push_back(b); }
        Bar const& getById(int id) { return m_vec[id]; }
      }

この例の問題は、クライアントがベクターに格納されている参照を呼び出して取得していることです。クライアントが新しい要素をたくさん挿入した後はどうなりますか?ベクターは内部でサイズを変更し、それらすべての参照に何が起こるかを推測する必要がありますか?そうです、無効です。これにより、&を削除するだけで修正されたバグを見つけるのが非常に困難になりました。

コードに問題はありません。参照およびSTLコンテナによる返品を誤解していますか、それとも投稿が正しくありませんか?

4

4 に答える 4

4

たとえば、ベクトルに2つの要素があるとします。

aおよびbr1これらとの参照を返しますr2

ここで、別のクライアントがベクターへの挿入を行います。ベクトルには2つの要素ストレージしかないためです。ストレージを再割り当てします。それらの後にコピーabて挿入cします。これにより、aとの場所が変更されbます。したがって、参照r1r2は無効になり、ジャンクの場所を指しています。

getByIdメソッドが参照によって返されない場合、コピーが作成され、すべてが正常に機能します。

于 2012-07-31T04:30:16.463 に答える
4

この問題は、次のように簡単に表示されます。

std::vector<int> vec;
vec.push_back(1);
const int& ref = vec[0];
vec.push_back(ref);

の内容vec[1]は未定義です。2番目push_backのは、初期化されたときrefのメモリ内の場所への参照です。vec[0]の内部ではpush_backvector再割り当てが必要になる可能性があり、それによって参照先が無効refになります。


これは大きな不便ですが、幸いなことに、あまり頻繁に発生する問題ではありません。Fooコンテナの人々は、 BarIDで見つけたものと同じものを挿入しますか?それは私には面白いようです。アクセスごとにコピーを作成するのは、問題を解決するにはやり過ぎのようです。あなたがそれが十分に悪いと思うなら、

void insert(const Bar& b)
{
    if ((m_vec.data() <= &b) && (&b < m_vec.data() + m_vec.size()))
    {
        Bar copy(b);
        return insert(copy);
    }
    else
        m_vec.push_back(b);
}

C ++ 11では、そのように書く方がはるかに良いでしょう(まともなmoveコンストラクターがあるとFoo::insert仮定して):Bar

void insert(Bar b)
{
    m_vec.emplace_back(std::move(b));
}
于 2012-07-31T05:04:13.403 に答える
2

他の回答に加えて、この効果はコンテナの種類によって異なることを指摘しておく価値があります。たとえば、ベクトルの場合、次のようになります。

ベクトルの再割り当ては、メンバー関数がベクトルオブジェクトに含まれるシーケンスを現在のストレージ容量を超えて増やす必要がある場合に発生します。他の挿入と消去は、シーケンス内のさまざまなストレージアドレスを変更する可能性があります。このようなすべての場合、シーケンスの変更された部分を指すイテレータまたは参照は無効になります。再割り当てが発生しない場合、挿入/削除ポイントの前のイテレータと参照のみが有効なままになります。

一方、リストはデータの保存方法により、この点でよりリラックスしています。

リストの再割り当ては、メンバー関数がリストの要素を挿入または消去する必要がある場合に発生します。このような場合はすべて、制御されたシーケンスの消去された部分を指すイテレータまたは参照のみが無効になります。

他のコンテナタイプについても同様です。

于 2012-07-31T06:17:50.947 に答える
1

同じ記事のコメントから:

ここでの問題は、参照による戻りが悪であるということではなく、参照を返す対象が無効になる可能性があるということです。

ページ153、「C ++標準ライブラリ:チュートリアルとリファレンス」のセクション6.2-Josuttis、次のように読みます。

「要素を挿入または削除すると、次の要素を参照する参照、ポインター、およびイテレーターが無効になります。挿入によって再割り当てが発生すると、すべての参照、ポインター、およびポインターが無効になります。」

コードサンプルは、ベクトルの最初の要素への参照を保持し、1000個の要素をベクトルに挿入してから、既存の参照を使用しようとするのと同じくらい邪悪です。

于 2018-01-17T10:22:47.640 に答える