In uno STL Mappa di struct, perché l'operatore “[]” causa dtor del struct da richiamare 2 volte in più?

StackOverflow https://stackoverflow.com/questions/4017892

Domanda

Ho creato un semplice caso di test esibendo uno strano comportamento che ho notato in una base di codice più grande su cui sto lavorando. Questo caso di test è al di sotto. Sto contando sulla STL operator "[]" Map per creare un puntatore ad una struct in una mappa di tali strutture. Nel caso di test al di sotto, la linea ...

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

... mi viene il puntatore (e crea una nuova voce nella mappa). La cosa strana che ho notato è che questa linea non solo provoca una nuova voce nella mappa da creare (a causa del l'operatore "[]"), ma per qualche ragione che provoca distruttore della struttura per essere chiamato due tempi supplementari. Sono ovviamente manca qualcosa - ogni aiuto è molto apprezzato! Grazie!

#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";
    }
};

il codice precedente uscite seguente ...

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

... ma non capisco che cosa causa i primi due invocazioni di distruttore di TestStruct? (Credo che l'ultima invocazione distruttore ha senso perché testStructMap sta andando fuori portata.)

È stato utile?

Soluzione

La funzionalità di std::map<>::operator[] è equivalente a

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

espressione, come specificato nella specifica del linguaggio. Questo, come si può vedere, coinvolge default-costruire un oggetto temporaneo di tipo T, copiandolo in un oggetto std::pair, che viene poi copiato (di nuovo) nel nuovo elemento della mappa (ammesso che non c'era già). Ovviamente, questo produrrà alcuni oggetti T intermedi. La distruzione di questi oggetti intermedi è quello che si osserva nel tuo esperimento. Ti manca la loro costruzione, dal momento che non genera alcun feedback da costruttore di copia della classe.

Il numero esatto di oggetti intermedi potrebbe dipendere funzionalità di ottimizzazione del compilatore, in modo che i risultati possono variare.

Altri suggerimenti

Hai alcune copie invisibili compiuti:

#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";
    }
};

Risultati in:

Marker One
TestStruct Constructor!
TestStruct copy Constructor!
TestStruct copy Constructor!
TestStruct Destructor!
TestStruct Destructor!
Marker Two
TestStruct Destructor!

aggiungere la seguente all'interfaccia di TestStruct:

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

I tuoi due chiamate distruttore misteriosi sono probabilmente in coppia con chiamate costruttore di copia corso da qualche parte all'interno del std::map. Ad esempio, è possibile che operator[] default-costruisce un oggetto TestStruct temporanea e quindi copia-costruisce nella posizione corretta nella mappa. La ragione per cui ci sono due chiamate destructor (e quindi probabilmente due chiamate costruttore di copia) è l'implementazione-specifica, e dipenderà dalla vostra compilatore e l'implementazione della libreria standard.

inserti operator[] al map se non esiste già un elemento lì.

Quello che manca è l'uscita per il costruttore di copia compilatore fornito nel vostro TestStruct, che viene utilizzato durante contenitore di pulizia. Aggiungere che la produzione, e dovrebbe tutto più senso.

EDIT: La risposta di Andrey mi ha spinto a dare un'occhiata alla fonte in Microsoft VC ++ <map> 10 di, che è qualcosa che si potrebbe anche fare per seguire questo attraverso in tutti i suoi dettagli scabrosi. Si può vedere la chiamata insert() a cui egli si riferisce.

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);
    }

in modo che la lezione è - non ha messo le strutture in una mappa, se vi preoccupate per loro ciclo di vita. Utilizzare i puntatori, o shared_ptrs ancora meglio per loro

È possibile controllare fuori attraverso questo più semplice codice.

#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;
}

Il sotto mostra il risultato che codificano (1) linea superiore marchi (1 default const) e (copia const 2 default) chiamate.

user const
default const        // here
default copy const   // here
default copy const   // here
dest
dest
dest
dest
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top