2

重複の可能性:
3 つのルールとは?

std::pairコンポーネントのデストラクタをどのくらい正確に呼び出しますか? クラスのインスタンスを に追加しようとしていますが、クラスstd::mapのデストラクタに関するエラーが発生しています。

質問/問題を次の非常に単純な例に絞り込みました。

以下では、構築時に配列をmy_class作成しint、破棄時に削除するだけです。どういうわけか、「二重削除」エラーが発生します。

//my_class.h
class my_class {
  public:
    int an_int;
    int *array;

    //constructors:
    my_class()
    {
      array = new int[2];
    }
    my_class(int new_int) : an_int(new_int)
    {
      array = new int[2];
    }

    //destructor:
    ~my_class()
    {
      delete[] array;
    }
};  //end of my_class

一方、main.cpp では...

//main.cpp
int main(int argc, char* argv[])
{
  std::map<int, my_class>   my_map;

  my_map.insert( std::make_pair<int, my_class> (1, my_class(71) ) );

  return 0;
} // end main

コンパイルは問題なく行われますが、これにより次のランタイム エラーが発生します。

*** glibc detected *** ./experimental_code: double free or corruption (fasttop):

または、valgrind を使用して:

==15258== Invalid free() / delete / delete[] / realloc()
==15258==    at 0x40249D7: operator delete[](void*) (vg_replace_malloc.c:490)
==15258==    by 0x8048B99: main (my_class.h:38)
==15258==  Address 0x42d6028 is 0 bytes inside a block of size 8 free'd
==15258==    at 0x40249D7: operator delete[](void*) (vg_replace_malloc.c:490)
==15258==    by 0x8048B91: main (my_class.h:38)

(コメントなどを切り取ったため、行番号はオフになっています)

私は何かが欠けているに違いないstd::pair...?

事前にすべてに感謝します!

4

4 に答える 4

10

my_classstl コンテナーに追加すると、コピー コンストラクターが呼び出されます。定義しないと、メンバーごとのコピーが行わmy_classれ、同じ int 配列を指す 2 つのオブジェクトが作成されます。これらが削除されると、同じ int 配列が 2 回削除される可能性があります。

3つのルールをご覧ください

C++11 では、効率が心配な場合はムーブ コンストラクターも参照してください。

于 2012-01-17T15:31:19.730 に答える
7

このクラスは、コピー コンストラクターと代入演算子を使用せずにデストラクターを定義することにより、3 の規則に違反しています。これらを定義すると、コードは正常に実行されるはずです。STL コンテナーはこれらに大きく依存しているため、STL コンテナーのテンプレート引数としてクラスを使用するたびに、3 つすべてを実装したかどうかを自問する必要があります。

于 2012-01-17T15:31:52.727 に答える
4

クラスのコピーは、コピーされたポインターのインスタンスを介して同じ配列を共有するため、適切なコピー コンストラクターを定義する必要があります。

于 2012-01-17T15:30:40.460 に答える
4

3 のルールは空想的です。通常、標準コンテナはより洗練されています。


問題は、配列がコピーされるのではなく、配列へのポインタがコピーされることです。2 つのインスタンスが同じポインターを保持している場合、同じ配列を 2 回削除することになります。

クラスに適切なコピー操作を定義することもできますが、通常、標準のコンテナーを使用すると、コピー、メモリの取得、メモリの解放、自己割り当て、例外の保証に関するすべての問題が解決されます。

  • std::vector動的配列のドロップイン置換として使用します。
  • std::array固定サイズの配列のドロップイン置換として使用します。

すべてのメンバーが適切なコピー セマンティクスを持っている場合、クラスは明示的なコピー操作を必要としないため、多くの作業を節約し、保守性を高め、エラーの可能性を減らします。

そう:

一般に、手動配列よりも標準コンテナーを優先します

class my_class {
public:
    my_class()
    : new_int(0), array(2)
    {}

    my_class(int new_int)
    : an_int(new_int), array(2)
    {}

private:
    int an_int;
    std::vector<int> array; // do not expose them
}; 

また

class my_class {
public:
    my_class()
    : new_int(0)
    {}

    my_class(int new_int)
    : an_int(new_int)
    {}

private:
    int an_int;
    std::array<int,2> array; // do not expose them
}; 

標準コンテナを省略しなければならない場合:

  • コピー コンストラクタを記述します。
  • コピー課題を書きます。また
  • 無断転載禁止。

その前に、ルール オブ スリーについて読み、自己代入の危険性に注意し、スワップ トリック(注: これは一般的な C++ のイディオムです) を理解し、例外の安全性について学びます (注:本の内容は、GotW シリーズの記事に無料で掲載されています)。

于 2012-01-17T15:33:47.790 に答える