views:

399

answers:

3

I know STL containers like vector copies the object when it is added. push_back method looks like:

void push_back ( const T& x );

I am surprised to see that it takes the item as reference. I wrote a sample program to see how it works.

struct Foo
{
    Foo()
    {
     std::cout << "Inside Foo constructor" << std::endl;
    }

    Foo(const Foo& f)
    {
     std::cout << "inside copy constructor" << std::endl;
    }
};

Foo f;
std::vector<Foo> foos;
foos.push_back(f);

This copies the object and I can see it is calling copy-constructor.

My question is, when the push_back takes item as reference, how it is calling copy-constructor? Or am I missing something here?

Any thoughts..?

+5  A: 

It probably uses "placement new" to construct the object in-place in its internal array. Placement new doesn't allocate any memory; it just places the object where you specify, and calls the constructor. The syntax is new (address) Class(constructor_arguments).

The copy constructor T::T(T const &) is called to create the copy in-place. Something like this (simplified):

template<T>
void vector<T>::push_back(T const &item) {
    // resize if necessary
    new (&d_array[d_size++]) T(item);
}

Note that T must have a copy constructor for this to work. By default (if you do nothing), it gets one for free. If you define it explicitly, it must be public for vector<T> to work.

Here's how GNU's libstdc++ does it, but I doubt that it'll be very enlightening. There is an allocator (the second template argument to vector) that makes it less straightforward.

Thomas
This is OK when T has got a parameterless constructor. But what will happen when it has a parameterized constructor? How vector can initialize new object?
Appu
This is OK when T has got a _copy constructor_. Which is does, by default, and if you implement it, unless you explicitly made it `private` or `protected`.
Thomas
I edited my answer to clarify.
Thomas
Thanks. I will investigate further.
Appu
Your explanation is clear. I am wondering, why they don't have a signature like `push_back(T const item)` and by avoiding the placement new.
Appu
This would make a copy of the item when it is passed into the function (because it's pass-by-value), which the pass-by-reference version avoids. It could avoid placement `new` by using the `=` operator, but that would require that there is already an initialized object in that place (to call `operator=` on) and thus require `T` to have a default constructor.
Thomas
The other advantage of using placement new is that the unused space which the vector has allocated, but which has not yet been filled, doesn't have to be initialised at all. This is a small optimisation if the default constructor of T exists and doesn't do much, but a significant difference if the default constructor of T does a lot of work, or has side-effects. And as Thomas points out, it allows T to have no no-args constructor at all.
Steve Jessop
Ok. Thanks guys.
Appu
+1  A: 

It uses the placement new operator and copy-constructs it to unitialized memory;

The placement new creates a new element at a specified adress in memory, in the vector case, the current end();

void push_back(const T& val){
::new (&*end()) T(val);
[increase end]
}

look at http://spotep.com/dev/devector.h which has quite clear code (as opposed to most STL implementations).

Viktor Sehr
+2  A: 

the c++ sdk always takes const T & as funtion parameter for efficience. In your case if it take T as parameter, the copy action will be done twice, one for passing it to function push_back(f), one for internal adding it to the container. And by taking const T& as parameter only one copy needed!

BeastToHuman
There is no C++ SDK.
GMan