8

作業中のより大きなコードベースで気付いた奇妙な動作を示す単純なテストケースを作成しました。このテストケースは以下のとおりです。私は、STLマップの「[]」演算子を使用して、そのような構造体のマップに構造体へのポインターを作成しています。以下のテストケースでは、次の行が...

TestStruct *thisTestStruct = &testStructMap["test"];

...ポインタを取得します(そしてマップに新しいエントリを作成します)。私が気付いた奇妙なことは、この行によってマップに新しいエントリが作成されるだけでなく( "[]"演算子のため)、何らかの理由で構造体のデストラクタが2回余分に呼び出されることです。私は明らかに何かが欠けています-どんな助けでも大歓迎です!ありがとう!

#include <iostream>
#include <string>
#include <map>

using namespace std;
struct TestStruct;

int main (int argc, char * const argv[]) {

    map<string, TestStruct> testStructMap;

    std::cout << "Marker One\n";

    //why does this line cause "~TestStruct()" to be invoked twice?
    TestStruct *thisTestStruct = &testStructMap["test"];

    std::cout << "Marker Two\n";

    return 0;
}

struct TestStruct{
    TestStruct(){
        std::cout << "TestStruct Constructor!\n";
    }

    ~TestStruct(){
        std::cout << "TestStruct Destructor!\n";
    }
};

上記のコードは次を出力します...

/*
Marker One
TestStruct Constructor!             //makes sense
TestStruct Destructor!               //<---why?
TestStruct Destructor!               //<---god why?
Marker Two
TestStruct Destructor!               //makes sense
*/

...しかし、TestStructのデストラクタの最初の2つの呼び出しの原因がわかりませんか?(testStructMapがスコープ外になるため、最後のデストラクタ呼び出しは理にかなっていると思います。)

4

7 に答える 7

18

の機能はstd::map<>::operator[]と同等です

(*((std::map<>::insert(std::make_pair(x, T()))).first)).second

言語仕様で指定されている式。ご覧のとおり、これにはデフォルトが含まれます。タイプの一時オブジェクトを作成し、Tそれをオブジェクトにコピーしstd::pairます。このオブジェクトは、後で(再び)マップの新しい要素にコピーされます(まだ存在しないと仮定します)。明らかに、これによりいくつかの中間Tオブジェクトが生成されます。これらの中間オブジェクトの破壊は、実験で観察したものです。クラスのコピーコンストラクターからフィードバックを生成しないため、それらの構築を見逃します。

中間オブジェクトの正確な数はコンパイラの最適化機能に依存する可能性があるため、結果は異なる場合があります。

于 2010-10-25T19:20:43.160 に答える
8

目に見えないコピーが作成されています。

#include <iostream>
#include <string>
#include <map>

using namespace std;
struct TestStruct;

int main (int argc, char * const argv[]) {

    map<string, TestStruct> testStructMap;

    std::cout << "Marker One\n";

    //why does this line cause "~TestStruct()" to be invoked twice?
    TestStruct *thisTestStruct = &testStructMap["test"];

    std::cout << "Marker Two\n";

    return 0;
}

struct TestStruct{
    TestStruct(){
        std::cout << "TestStruct Constructor!\n";
    }

    TestStruct( TestStruct const& other) {
        std::cout << "TestStruct copy Constructor!\n";
    }

    TestStruct& operator=( TestStruct const& rhs) {
        std::cout << "TestStruct copy assignment!\n";
    }

    ~TestStruct(){
        std::cout << "TestStruct Destructor!\n";
    }
};

結果:

Marker One
TestStruct Constructor!
TestStruct copy Constructor!
TestStruct copy Constructor!
TestStruct Destructor!
TestStruct Destructor!
Marker Two
TestStruct Destructor!
于 2010-10-25T19:11:53.900 に答える
5

TestStructのインターフェースに以下を追加します。

TestStruct(const TestStruct& other) {
    std::cout << "TestStruct Copy Constructor!\n";
}   
于 2010-10-25T19:08:26.373 に答える
4

operator[]mapそこに要素がまだない場合はに 挿入します。

不足しているのは、TestStructコンテナのハウスキーピング中に使用される、コンパイラが提供するコピーコンストラクタの出力です。その出力を追加すれば、すべてがより理にかなっているはずです。

編集:Andreyの回答により、Microsoft VC ++ 10のソースを確認するように促されました<map>。これは、これを詳細に追跡するためにも実行できます。insert()あなたは彼が言及している呼び出しを見ることができます。

mapped_type& operator[](const key_type& _Keyval)
    {   // find element matching _Keyval or insert with default mapped
    iterator _Where = this->lower_bound(_Keyval);
    if (_Where == this->end()
        || this->comp(_Keyval, this->_Key(_Where._Mynode())))
        _Where = this->insert(_Where,
            value_type(_Keyval, mapped_type()));
    return ((*_Where).second);
    }
于 2010-10-25T19:08:29.947 に答える
4

2つの不思議なデストラクタ呼び出しは、おそらく内のどこかで行われているコピーコンストラクタ呼び出しとペアになっていますstd::map。たとえば、operator[]デフォルトで一時TestStructオブジェクトを作成し、それをマップ内の適切な場所にコピーして作成することが考えられます。2つのデストラクタ呼び出し(したがって、おそらく2つのコピーコンストラクタ呼び出し)がある理由は実装固有であり、コンパイラと標準ライブラリの実装によって異なります。

于 2010-10-25T19:08:39.307 に答える
0

したがって、レッスンは-構造体のライフサイクルを気にする場合は、構造体をマップに配置しないでください。ポインタを使用するか、それらへのより良いshared_ptrsを使用します

于 2010-10-25T21:19:15.290 に答える
0

このより単純なコードで確認できます。

#include <iostream>
#include <map>

using namespace std;

class AA
{
public:
  AA()            { cout << "default const" << endl; }
  AA(int a):x(a)  { cout << "user const" << endl; }
  AA(const AA& a) { cout << "default copy const" << endl; }
  ~AA()           { cout << "dest" << endl; }
private:
  int x;
};

int main ()
{
  AA o1(1);

  std::map<char,AA> mymap;

  mymap['x']=o1;    // (1)

  return 0;
}

以下の結果は、上記の(1)ラインコードが(1つのデフォルトのconst)および(2つのデフォルトのcopy const)呼び出しを行うことを示しています。

user const
default const        // here
default copy const   // here
default copy const   // here
dest
dest
dest
dest
于 2016-12-07T00:39:24.403 に答える