4
#include<iostream>
#include<vector>
#include<list>
#include<queue>
#include<map>
using namespace std;
class dog{
    public:
        string name;
        dog();
        dog(const dog & d);
        void barkname(){
            cout<<"bark "<<name<<endl;
        }
        virtual ~dog(){
            //cout<<"delete dog "<<name<<endl;
        }
};

dog::dog(){
    cout<<"blank dog"<<endl;
    this->name="blank";
}

dog::dog(const dog &d){
    cout<<"copy dog"<< " "+d.name<<endl;
    string temp=d.name;
    this->name=temp+" copied";
}


int main(){
    dog d;
    d.name="d";
    dog dd;
    dd.name="dd";
    dog ddd;
    ddd.name="ddd";
    vector<dog> doglist;
    doglist.push_back(d);
    doglist.push_back(dd);
    doglist.push_back(ddd);
    return 0;
}

Hello, I'm new to cpp. I tried to use copy constructor in my class dog. I pushed three dogs into the vector, using push_back three times. So I expected copy constructor to be called three times. However, after executing the code, I found that copy constructor was called six times, with the following results:

blank dog
blank dog
blank dog
copy dog d
copy dog dd
copy dog d copied
copy dog ddd
copy dog d copied copied
copy dog dd copied

I'm quite confused about why the dog is copied so many times. I only excute push_back for three times. Thank you.

Thank you for pointing out a similar question: why the copy-constructor is called twice when doing a vector.push_back

In this post, the author only push_back one object, but copy constructor got called twice. However, In my case, when I call push_back once, copy constructor got called only once. I have understood where my problem is, thank you all for your help.

Community
  • 1
  • 1
Casualet
  • 295
  • 3
  • 12
  • Possible duplicate of [why the copy-constructor is called twice when doing a vector.push\_back](http://stackoverflow.com/questions/30358475/why-the-copy-constructor-is-called-twice-when-doing-a-vector-push-back) – XCS Jul 03 '16 at 12:41
  • @Cristy: Not really, no. – Lightness Races in Orbit Jul 03 '16 at 12:42
  • @LightnessRacesinOrbit How are the questions different? :D – XCS Jul 03 '16 at 12:43
  • What happens if you declare vector doglist(3)? – jarmod Jul 03 '16 at 12:44
  • @jarmod: Then you end up with six dogs, and the same behaviour. – Lightness Races in Orbit Jul 03 '16 at 12:45
  • @LightnessRacesinOrbit Care to elaborate? They ask exactly the same question: "why is a copy constructor called multiple times when pushing_back". The answers are also the same. From the second answer there: `a copy is being made when [...] the vector is resized internally.` – XCS Jul 03 '16 at 12:54
  • @Cristy: In that question, the OP is only adding one object to the vector. It's unclear to me what Richard Hodges means by *"a copy is being made when [...] the vector is resized internally"* -- But if it's referring to the same type of copy that is referred to in LRiO's answer, then it's wrong. There would be absolutely no reason to add the object to the vector (requiring a copy), and then reallocate *again* (requiring another copy). – Benjamin Lindley Jul 03 '16 at 13:03

2 Answers2

17

The vector needs some place to put your dogs, so it allocates memory for them. But it cannot allocate infinite memory. As you add more dogs, the vector needs to allocate a bigger block of memory, and every time it does that it must relocate your dogs to their new home. The only way to do that, with your class as it is currently designed, is to copy them, then put the originals to sleep.

If you'd reserved enough space for all the dogs in the first place (as shown below), then this wouldn't have been necessary and your dogs could have gotten on with the business of running about being a right nuisance, without the distraction of constantly moving house.

doglist.reserve(3);
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • 4
    Lol: "it must relocate your dogs to their new home" :) – XCS Jul 03 '16 at 12:42
  • 1
    And it reallocates every time? I heard vectors preallocate several entries. Also can't they use move constructors, if they existed? (they don't in this case, I'm just asking) – Paul Stelian Jul 03 '16 at 12:43
  • 3
    @PaulStelian: Not every time - [growth rates differ but are typically geometric](http://stackoverflow.com/q/5404489/560648). And yes elements will be moved where possible. – Lightness Races in Orbit Jul 03 '16 at 12:44
  • Making the dogs relocatable is another option – M.M Jul 03 '16 at 12:51
1

The output will be more clear if to add statements that display the capacity of the vector. For example

#include <iostream>
#include <string>
#include <vector>

using namespace std;

class dog{
    public:
        string name;
        dog();
        dog(const dog & d);
        void barkname(){
            cout<<"bark "<<name<<endl;
        }
        virtual ~dog(){
            //cout<<"delete dog "<<name<<endl;
        }
};

dog::dog(){
    cout<<"blank dog"<<endl;
    this->name="blank";
}

dog::dog(const dog &d){
    cout<<"copy dog"<< " "+d.name<<endl;
    string temp=d.name;
    this->name=temp+" copied";
}


int main()
{
    dog d;
    d.name="d";
    dog dd;
    dd.name="dd";
    dog ddd;
    ddd.name="ddd";
    vector<dog> doglist;

    cout << "\nInitial capacity: " << doglist.capacity() << endl;

    doglist.push_back(d);

    cout << "After adding the first dog capacity: " << doglist.capacity() << endl;

    doglist.push_back(dd);

    cout << "After adding the second dog capacity: " << doglist.capacity() << endl;

    doglist.push_back(ddd);

    cout << "After adding the second dog capacity: " << doglist.capacity() << endl;

    return 0;
}

The program output is

blank dog
blank dog
blank dog

Initial capacity: 0
copy dog d
After adding the first dog capacity: 1
copy dog dd
copy dog d copied
After adding the second dog capacity: 2
copy dog ddd
copy dog d copied copied
copy dog dd copied
After adding the second dog capacity: 4

The output can differ depending on the vector implementation.

Considering the output you can see that initially the vector does not allocate memory for potentially added elements. Its capacity is equal to 0.

When the first item is added the vector allocates memory for this one item and copies the supplied object in this memory.

When the second item is added then the vector allocates a new extent of memory and copies the new element and the first one from the current memory extent to the new memory extent and so on.

You could say the vector that it would initially reserve memory for three items.

For example

#include <iostream>
#include <string>
#include <vector>

using namespace std;

class dog{
    public:
        string name;
        dog();
        dog(const dog & d);
        void barkname(){
            cout<<"bark "<<name<<endl;
        }
        virtual ~dog(){
            //cout<<"delete dog "<<name<<endl;
        }
};

dog::dog(){
    cout<<"blank dog"<<endl;
    this->name="blank";
}

dog::dog(const dog &d){
    cout<<"copy dog"<< " "+d.name<<endl;
    string temp=d.name;
    this->name=temp+" copied";
}


int main()
{
    dog d;
    d.name="d";
    dog dd;
    dd.name="dd";
    dog ddd;
    ddd.name="ddd";
    vector<dog> doglist;
    doglist.reserve( 3 );
    //^^^^^^^^^^^^^^^^^^^

    cout << "\nInitial capacity: " << doglist.capacity() << endl;

    doglist.push_back(d);

    cout << "After adding the first dog capacity: " << doglist.capacity() << endl;

    doglist.push_back(dd);

    cout << "After adding the second dog capacity: " << doglist.capacity() << endl;

    doglist.push_back(ddd);

    cout << "After adding the second dog capacity: " << doglist.capacity() << endl;

    return 0;
}

In this case the output will look like

blank dog
blank dog
blank dog

Initial capacity: 3
copy dog d
After adding the first dog capacity: 3
copy dog dd
After adding the second dog capacity: 3
copy dog ddd
After adding the second dog capacity: 3

So in this case the vector copies only the newly added item in the pre-allocated memory extent.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335