简体   繁体   中英

How does std::map call value destructors?

I have this code :

#include <iostream>
#include <map>
using namespace std;

class test{
   public:
   test() {
     cout << "calling test ctor " << endl;
   }

   ~test() {
      cout << "calling test dtor " << endl;
   }

   void callme(){
      cout << "call me " << endl;
   }
};

int main () {
   map<int, test> mp;
   mp[0].callme();
   mp[0].callme();
   return 0;
}

The output of this program is :

calling test ctor 
calling test dtor 
calling test dtor 
call me 
call me 
calling test dtor 

I am little confused how std::map is handling test:: ctors and dtors here.

Before executing this code, my assumption was that mp[0].callme() would create a new test object and call callme() on that, and if we call mp[0].callme() again, then it should call test:: dtor (since we are replacing the key 0 here) and then test:: ctor to create a new test object so that it could call callme() on that. Obviously my assumption is wrong here because output doesn't match at all.

Could anyone please throw some light on this ?

EDIT1 :

gcc --version = gcc (GCC) 5.1.1 20150422 (Red Hat 5.1.1-1)

Command to compile:

g++ maps.cpp

So, no flags with g++. Simple compile.

By compiling using the command g++ maps.cpp , you're invoking g++ in C++03 mode, which means it isn't able to use move semantics.

The relevant lines of the map::operator[] implementation can be found here

    if (__i == end() || key_comp()(__k, (*__i).first))
#if __cplusplus >= 201103L
      __i = _M_t._M_emplace_hint_unique(__i, std::piecewise_construct,
                        std::tuple<const key_type&>(__k),
                        std::tuple<>());
#else
      __i = insert(__i, value_type(__k, mapped_type()));
#endif

So prior to C++11, the mapped_type ( test in your example) is default constructed to create a value_type ( pair<int const, test> in your example). This is the initial call to the constructor.

The call to insert then has to copy the mapped type at least once when it inserts it into the internal storage for the map . Evidently, the libstdc++ implementation results in an additional copy somewhere, adding up to two copy constructions, and hence two matching destructor calls. If you add a copy constructor definition you'll see the number of destructor calls match the number of constructor calls.

Live demo

Also, notice that by adding the -std=c++11 flag, you avoid the intermediate copies. As seen in the code above, the implementation uses piecewise construction of the pair in that case to directly construct the mapped_type (and key_type ) in the map 's internal storage.

Apparently, your operator[] uses the following logic:

  1. If the object doesn't exist, create it and then set it equal to a default-constructed object.

  2. Return a reference to the object.

That is very strange behavior. The extra construction and assignment is not needed. The new object should just be default constructed in the first place.

For completeness:

#include <iostream>
#include <map>
using namespace std;

class test{
   public:
   test() {
     cout << "calling test ctor " << endl;
   }

   test(const test&) {
     cout << "calling copy ctor " << endl;
   }

//   test(test&&) {
//     cout << "calling move ctor " << endl;
//   }

   test& operator = (const test&) {
     cout << "calling copy assignment " << endl;
     return * this;
   }

//   test& operator = (test&&) {
//     cout << "calling move assignment " << endl;
//     return * this;
//   }

   ~test() {
      cout << "calling test dtor " << endl;
   }

   void callme(){
      cout << "call me " << endl;
   }
};

int main () {
   map<int, test> mp;
   mp[0].callme();
   mp[0].callme();
   return 0;
}

Compiled without C++11 gives:

calling test ctor 
calling copy ctor 
calling copy ctor 
calling test dtor 
calling test dtor 
call me 
call me 
calling test dtor 

Having comments on move semantics removed and compiled with C++11:

calling test ctor 
call me 
call me 
calling test dtor 

Both are compiled with g++ 4.8.4

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM