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.
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:
If the object doesn't exist, create it and then set it equal to a default-constructed object.
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.