three times copy ctor called, one ctor called, why?

A

Apricot

#include <iostream>
#include <string>
#include <map>
using namespace std ;
class tst
{
public :
tst() { cout << "tst::constructor" << endl ; }
tst(const tst & that) { cout << "tst::copy constructor" << endl ;
}
tst & operator =( const tst & that ) { cout << "tst::eek:perator ="
<< endl ; }

~tst() { cout << "tst::destructor" << endl ; }
} ;
int main()
{
map < string , tst > a ;
tst e ;
a.insert(pair < string , tst > ("standard",e) ) ;

system("pause") ;
return 0 ;
}
///// Output ////////////
tst::constructor
tst::copy constructor
tst::copy constructor
tst::copy constructor
tst::destructor
tst::destructor
 
K

Kevin Goodsell

Apricot said:
#include <iostream>
#include <string>
#include <map>
using namespace std ;
class tst
{
public :
tst() { cout << "tst::constructor" << endl ; }
tst(const tst & that) { cout << "tst::copy constructor" << endl ;
}
tst & operator =( const tst & that ) { cout << "tst::eek:perator ="
<< endl ; }

Why have you not returned anything from this function?
~tst() { cout << "tst::destructor" << endl ; }
} ;
int main()
{
map < string , tst > a ;
tst e ;
a.insert(pair < string , tst > ("standard",e) ) ;

Actually, that should be 'const string'. I think it should be OK this
way, but I seem to recall some implementations having trouble inserting
into a map this way, or by using make_pair. For that reason, using
map<>::value_type (which you can be sure has exactly the right type) may
be preferable. I'd probably do this:

typedef map<string, tst> MapType;
MapType a;
tst e;
a.insert(MapType::value_type("standard", e));
system("pause") ;
return 0 ;
}
///// Output ////////////
tst::constructor
tst::copy constructor
tst::copy constructor
tst::copy constructor
tst::destructor
tst::destructor

I don't see these results. What I do see might shed some light, however:

tst::constructor
tst::copy constructor
tst::copy constructor
tst::copy constructor
tst::destructor
tst::destructor
pause: not found
tst::destructor
tst::destructor

Notice that the ill-fated "pause" command came before two of the
destructor calls. Perhaps your pausing is preventing you from seeing
some of the destructor calls. If not that, then I suspect your compiler
is broken and due for an update.

-Kevin
 
A

Amit Manocha

You are expecting a single copy ctor, because e is passed to a.insert.
but it should be called one more time - ("standard", e) to pair <T1,
T2> conversion. now why it is being called third time probably is due
to internal pair implementation (object is copied somewhere in temp
object) ?

Amit Manocha
 
M

Michiel Salters

Kevin Goodsell said:
I don't see these results. What I do see might shed some light, however:

tst::constructor
tst::copy constructor
tst::copy constructor
tst::copy constructor
tst::destructor
tst::destructor
pause: not found
tst::destructor
tst::destructor

Notice that the ill-fated "pause" command came before two of the
destructor calls. Perhaps your pausing is preventing you from seeing
some of the destructor calls. If not that, then I suspect your compiler
is broken and due for an update.

Sorry for the long quote, but isn't this obvious? e is destroyed after
the pause, and the single tst in a is also destroyed later.

Regards,
Michiel Salters
 
V

velthuijsen

Apricot said:
#include <iostream>
#include <string>
#include <map>
using namespace std ;
class tst
{
public :
tst() { cout << "tst::constructor" << endl ; }
tst(const tst & that) { cout << "tst::copy constructor" << endl ;
}
tst & operator =( const tst & that ) { cout << "tst::eek:perator ="
<< endl ; }

~tst() { cout << "tst::destructor" << endl ; }
} ;
int main()
{
map < string , tst > a ;
tst e ;
a.insert(pair < string , tst > ("standard",e) ) ;

system("pause") ;
return 0 ;
}
///// Output ////////////
tst::constructor
tst::copy constructor
tst::copy constructor
tst::copy constructor
tst::destructor
tst::destructor

From what I understand all the STL containers get populated by
copying. This means that the only time the constructor gets called is
when you do
tst e;


a.insert(pair < string , tst > ("standard",e) );
First you create a (temporary) pair which gets populated by copying
("standard", e) into it.

In the second step the internal workings of the map implementation you
seem to be using creates a pair for internal use (2nd temporary in
this case) by copying ("standard", e) from the first pair into it.

Finally at the third step the map creates a node in the binary tree
that is populated by copying the 2nd temporary pair into it. (Note: it
is not required that any implementation uses a binary tree to store
map data, it just happens that all the implementations I know do it
that way)

fourth step is destroying the 2nd temporary pair (as seen by your
tst::destructor output), the one created by the insert action

fifth step is destroying the first temporary pair, the one you
explicitly created.


Hopefully the next bit of code can make it a bit clearer. replace your
main with it (it should also give you two more destructor calls).
Code:
int main()
{
{
map < string , tst > a;
cout << "map created\n" ;
tst e ;
cout << "tst e created\n" ;
std::pair<string, tst> mep("standard", e);
cout << "pair created\n";
a.insert(mep) ;
}

cout << "mash keyboard to continue" << endl;
cin.get();
return 0 ;
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,011
Latest member
AjaUqq1950

Latest Threads

Top