3

このチュートリアルを読んだ後に提起されたこの質問: http://www.cprogramming.com/tutorial/auto_ptr.html

そこでは、次のステートメントを見つけることができます。たとえば、標準テンプレート ライブラリで auto _ptr オブジェクトを使用すると、STL の一部の関数がベクター コンテナー クラスなどのコンテナー内のオブジェクトのコピーを作成する可能性があるため、問題が発生する可能性があります。1 つの例は、並べ替え対象のコンテナー内のオブジェクトの一部のコピーを作成する sort 関数です。結果として、このコピーはコンテナ内のデータを簡単に削除できます!

'auto_ptr' に関する論文のほとんどは、次のようなことを教えてくれsortますstd::vector

したがって、私の目標は、この点を説明するコード サンプルを作成すること、またはそのような例が理論的に正しく、実際には奇妙であることを証明することです。

PS @everybody_who_also_knows_that_auto_ptr_is_deprecated 私これを知っています。しかし、新しいポインター コンテナーを使用できない可能性がある技術的な理由 (レガシー コードまたは古いコンパイラー) を考慮していませんか? さらに、この質問は古いものと悪いものについてです (もしよろしければ) auto_ptr.

4

5 に答える 5

4

私は現在 MSVC を持っていませんが、g++ からのエラーから判断すると、これが理由だと思います:

auto_ptr<T>変更可能な参照を取る「コピー コンストラクター」のみがあります (§D.10.1.1[auto.ptr.cons]/2–6):

auto_ptr(auto_ptr& a) throw();
template<class Y> auto_ptr(auto_ptr<Y>& a) throw();

ただしvector::push_back、const 参照は受け入れます (§23.3.6.1[vector.overview]/2)。

void push_back(const T& x);

そのため、const 参照を取るコンストラクターがないため、push_back を介して auto_ptr を構築することはできません。

于 2011-12-25T17:46:49.183 に答える
1

ステップ 1 この問題を簡単に解決しましょう。

#include <iostream>
#include <vector>
#include <algorithm>

template<> struct std::less<std::auto_ptr<int>>: public std::binary_function<std::auto_ptr<int>, std::auto_ptr<int>, bool> {
  bool operator()(const std::auto_ptr<int>& _Left, const std::auto_ptr<int>& _Right) const
  { // apply operator< to operands
    return *_Left < *_Right;
  }
};

int wmain() {
  using namespace std;

  auto_ptr<int> apai(new int(1)), apai2(new int(2)), apai3(new int(3));
  vector<auto_ptr<int>> vec;
  vec.push_back(apai3);
  vec.push_back(apai);
  vec.push_back(apai2);

  for ( vector<auto_ptr<int>>::const_iterator i(vec.cbegin()) ; i != vec.cend() ; ++i )
    wcout << i->get() << L'\t';

  vector<int> vec2;
  vec2.push_back(3);
  vec2.push_back(2);
  vec2.push_back(5);

  sort(vec2.begin(), vec2.end(), less<int>());

  sort(vec.begin(), vec.end(), less<auto_ptr<int>>());

  return 0;
}

MSVCPP11 のエラー テキストは次のとおりです: _エラー 1 エラー C2558: クラス 'std::auto _ptr< Ty>': コピー コンストラクターが使用できないか、コピー コンストラクターが '明示的に宣言されています' c:\program files (x86)\microsoft visual studio 11.0\vc\include\xmemory0 608

結論は次のとおりです。そのような例をコンパイルすることさえできません。なぜ彼らは私がコンパイルできないことをするのを妨げているのですか?? 彼らの予防策は必ずしも正しいとは限りません。


ステップ2

設計上、要素型auto_ptrとしてvector直接使用することはできません。auto_ptrしかし、以下に示す方法で「auto_ptr」をラップすることができます。

#include <iostream>
#include <vector>
#include <algorithm>
#include <memory>
#include <functional>

template<typename T> class auto_ptr_my: public std::auto_ptr<T> {
public:
  explicit auto_ptr_my(T *ptr = 0) {
    this->reset(ptr);
  }
  auto_ptr_my<T> &operator=(const auto_ptr_my<T> &right) {
    *(static_cast<std::auto_ptr<T> *>(this)) = *(static_cast<std::auto_ptr<T> *>(const_cast<auto_ptr_my *>(&right)));
    return *this;
  }
  auto_ptr_my(const auto_ptr_my<T>& right) {
    *this = right;
  }
};

namespace std
{
template<> struct less<auto_ptr_my<int> >: public std::binary_function<auto_ptr_my<int>, auto_ptr_my<int>, bool> {
  bool operator()(const auto_ptr_my<int>& _Left, const auto_ptr_my<int>& _Right) const
  { // apply operator< to operands
    return *_Left < *_Right;
  }
};
}

int wmain() {
  using namespace std;

  auto_ptr_my<int> apai(new int(1)), apai2(new int(2)), apai3(new int(3));

  vector<auto_ptr_my<int>> vec;
  vec.push_back(apai3);
  vec.push_back(apai);
  vec.push_back(apai2);

  for ( vector<auto_ptr_my<int>>::const_iterator i(vec.cbegin()) ; i != vec.cend() ; ++i )
    wcout << **i << L'\t';

  sort(vec.begin(), vec.end(), less<auto_ptr_my<int>>());

  for ( vector<auto_ptr_my<int>>::const_iterator i(vec.cbegin()) ; i != vec.cend() ; ++i )
    wcout << **i << L'\t';

  return 0;
}

このコードは、メモリ リークやクラッシュの有無にかかわらず使用できることを示しauto_ptrますvectorsort


ステップ 3 KennyTM が以下に投稿したように:

return 0;ステートメントの前に次のコードを追加します。

std::vector<auto_ptr_my<int>> vec2 = vec;

for ( vector<auto_ptr_my<int>>::const_iterator i(vec2.cbegin()) ; i != vec2.cend() ; ++i )
  wcout << **i << L'\t';
wcout << std::endl;

for ( vector<auto_ptr_my<int>>::const_iterator i(vec.cbegin()) ; i != vec.cend() ; ++i )
  wcout << **i << L'\t';
wcout << std::endl;

...そしてメモリリークが発生します!


結論目に見えないクラッシュなしauto_ptrでコンテナーを 使用できる場合もあれば、そうでない場合もあります。とにかく悪い習慣です。ただし、STL コンテナーやアルゴリズムで直接使用できないように設計されていることを忘れないでください。これに対して、ラッパー コードを作成する必要があります。最後に、STL コンテナーでの使用は自己責任で行ってください。たとえば、一部の実装では要素の処理中にクラッシュが発生しませんが、他の実装では直接クラッシュが発生します。auto_ptrauto_ptrsortvector

この質問には学術的な目的があります。STEP 3 のクラッシュ例を提供してくれた KennyTM に感謝します。

于 2011-12-26T05:03:05.247 に答える
1

auto_ptrあなたが書いたものから、 s のコンテナーとそれらが安全でない理由について知っておくべきことはすべて既に知っているようです。

したがって、あなたがauto_ptrs のコンテナに興味を持っているのは、純粋に教えることだけだと思います。意図的な反例を構築しようとするあなたの不満を理解しています。実際、標準コンテナのほとんどの実装者は、auto_ptrs の壊れたセマンティクスを誤ってトリガーしないように回避策を講じています。

だから、ここに私が教えるために正確に書いた例があります:

class MyClass {
  int a;
public:
  MyClass (int i) : a(i) {  }
  int get() const { return a; }
};

int main() {
  constexpr unsigned size = 10;
  std::vector< std::auto_ptr<MyClass> > coap;
  coap.resize(size);

  for (unsigned u=0; u<size; u++)
    coap[u] = std::auto_ptr<MyClass>( new MyClass( rand() % 50 ));

  std::sort( coap.begin(), coap.end(),
           []( std::auto_ptr<MyClass> a,
               std::auto_ptr<MyClass> b) { return a->get() < b->get(); }); 
}

g++ 4.9.2 でコンパイルすると、うまくセグメンテーション違反する実行可能ファイルが生成されます。

上記の例は、型推論を使用してさらに簡潔に書き直すことができます。

  std::sort( coap.begin(), coap.end(),
           []( auto a, auto b) { return a->get() < b->get(); }); 

問題は の特定の実装にあるのではないことに注意してください。これは安全std::sortだと思われますauto_ptr。に渡す比較ラムダ関数ではstd::sort、値によって引数を意図的に受け入れるため、比較が実行されるたびにコンテナー内のオブジェクトが破棄されます。

以下に示すように、参照によって引数を受け取るようにラムダを変更した場合、概念的に間違ったことをしている場合でも、ほとんどの STL 実装は実際には正しく動作します。

  std::sort( coap.begin(), coap.end(),
           []( const std::auto_ptr<MyClass> & a,
               const std::auto_ptr<MyClass> & b) { return a->get() < b->get(); }); 

幸運を!

于 2016-07-11T14:31:20.363 に答える
0

結論は次のとおりです。そのような例をコンパイルすることさえできません。なぜ彼らは私がコンパイルできないことをするのを妨げているのですか??

IIRC、それは逆です。コンパイラベンダーは、実行できないはずのコンパイルを防ぐための措置を講じます。標準が書かれている方法では、コードがコンパイルされる方法でライブラリを実装した後、適切に動作しない可能性があります彼らはまた、この方法でそれを実装することができます.これは、コンパイラが実際に何か愚かなことをするのを防ぐことができる数少ない時間の1つであるため、優れていると見なされます:)

于 2011-12-25T18:35:20.650 に答える
-2

正しい答えは、「auto_ptr をまったく使用しない」です。これは非推奨であり、標準の一部にはなりませんでした。まさにここで概説した理由によるものです。代わりに std::unique_ptr を使用してください。

于 2011-12-25T19:53:35.043 に答える