6

私は最近、コピーアンドスワップイディオムとは何ですか?に関する StackOverflow の回答を読みました。そして、コピーアンドスワップのイディオムができることを知っていました

コードの重複を回避し、強力な例外保証を提供します。

しかし、SGI STLのdeque 実装を調べてみると、イディオムを使用していないことがわかりました。イディオムがどういうわけか「ベストプラクティス」のようなものなら、なぜだろうと思いますか?

  deque& operator= (const deque& __x) {
    const size_type __len = size();
    if (&__x != this) {
      if (__len >= __x.size())
        erase(copy(__x.begin(), __x.end(), _M_start), _M_finish);
      else {
        const_iterator __mid = __x.begin() + difference_type(__len);
        copy(__x.begin(), __mid, _M_start);
        insert(_M_finish, __mid, __x.end());
      }
    }
    return *this;
  }       
4

5 に答える 5

8

あなたが示すコードは、コンテナが大きくなる必要がない限り、メモリを再割り当てしません。これは大幅な節約になる可能性があります。コピー アンド スワップは常にメモリを割り当ててコピーを実行し、次に既存の要素のメモリを解放します。

deque<vector<int>>両端キューの既存のベクトル メンバーの容量が大きい場合を考えてみましょう。

deque<vector<int>> d(2);
d[0].reserve(100);
d[1].reserve(100);

SGI STL 実装を使用すると、各要素に割り当てることでその容量が維持されるため、ベクトルを大きくする必要がある場合は、何も割り当てずにそれを行うことができます。

d = deque<vector<int>>(2);
assert(d[0].capacity() >= 100);
assert(d[1].capacity() >= 100);
d[0].push_back(42);  // does not cause an allocation

コピー アンド スワップは、既存のメンバーを予備容量のない新しい要素に置き換えるため、上記のアサーションは失敗し、push_backメモリを割り当てる必要があります。これは、すでに存在する完全に良好なメモリを使用する代わりに、割り当て解除と再割り当てに時間を浪費します。

コピー アンド スワップは、例外の安全性と正確性を非常に簡単に実現するための便利な方法ですが、必ずしも可能な限り効率的であるとは限りません。STL や C++ 標準ライブラリのようなコードでは、実装を少し簡単にするために効率を犠牲にしたくありません。通常、そのようなコードは、「難しい方法」で例外の安全性を確保できる専門家が作成する必要があります。 「最も便利な方法だけではありません。

于 2015-05-22T13:24:16.387 に答える
4

イディオムは強力な例外保証を提供する簡単な方法であり、そうしない正当な理由がない限り、良いことをするという意味での「ベスト プラクティス」です。

ただし、コストがかかります。2 番目のオブジェクトを作成して破棄する必要があります。これにはコストがかかる可能性があり、大量のメモリ割り当てが必要な場合は、ピーク時のメモリ使用量が必要以上に増加する可能性があります。それが懸念される場合、またはこのような汎用ライブラリを作成している場合は、一時オブジェクトを作成せずにオブジェクトをコピーする他の方法を見つける必要があります。例外の安全性には、単純なイディオムを使用する場合よりも注意が必要です。

于 2015-05-22T13:27:03.867 に答える
3

コピー アンド スワップはベスト プラクティスですが、場合によっては効率が低下する可能性があります。この特定のケースでは、コードは、割り当てられているベクトルに十分なメモリが既にある場合、追加のメモリ割り当てを行わずに値をコピーできるという事実を利用しています。

于 2015-05-22T13:23:45.523 に答える
2

この場合、これは過剰な再割り当てを避けるために行われると思います: コピー アンド スワップでは、コピーを割り当てる必要があり (したがって、一度に 2 倍のストレージを使用します)、次にすべての deque のメモリの割り当てを解除する必要があります。このコードは、copy-from が大きく、その差のみが必要な場合にのみ割り当てます。

これは実際にはベクターではなくデキューに適用されます - vector::operator= はどのように実装されていますか?

さらに、それはイディオムが普及する前に書かれた可能性があります。そして今でもそれは普遍的に受け入れられていません。

于 2015-05-22T13:29:06.390 に答える
1

コピー/スワップの使用を主張する強力な保証ではなく、基本的な例外保証 (つまり、例外が発生した場合にコンテナーが有効な状態のままになること)std::deque<T>::operator=(const std::deque<T>&)を提供する必要がある標準的な命令

于 2015-05-22T13:29:35.407 に答える