Rıfat Tolga Kiran
Rıfat Tolga Kiran

Reputation: 251

too many copy ctors i cant figure out

#include <iostream>
#include <cstring>
#include <vector>

using namespace std;

class items{
    char * name;
public:
    items(const char * str){
        int len = strlen(str);
        this->name = new char [len];
        strcpy(this->name,str);
        cout << "Default ctor " << this->name << " \t@" << (this) << endl;
    }

    items(const items& obj){
        int len = strlen(obj.name);
        this->name = new char [len];
        strcpy(name,obj.name);
        cout << "Copy ctor " << this->name << " \t@" << this << endl;
    }

    ~items(){
        cout << "dtor \t" << this->name << "\t@" << this << endl;
        delete [] name;
    }

    const char * getName() const{
        return this->name;
    }
};

ostream& operator<<(ostream& stream, const items& obj){
    stream << obj.getName();
    return stream;
}

int main(int argc, char ** argv){
    items Ruler("Ruler"), Pencil("Pencil"), Book("Book"), Notebook("Notebook"), Sharpener("Sharpener");


    vector<items> school;
    school.push_back(Ruler);
    school.push_back(Pencil);
    school.push_back(Book);

    return 0;
}

i have bizarre results. Can you explain what is happening behind the scene? Results:

Default ctor Ruler  @0x62ff1c
Default ctor Pencil     @0x62ff18
Default ctor Book   @0x62ff14
Default ctor Notebook   @0x62ff10
Default ctor Sharpener  @0x62ff0c
Copy ctor Ruler     @0x9cd1d0
Copy ctor Pencil    @0x9cd504
Copy ctor Ruler     @0x9cd500
dtor    Ruler   @0x9cd1d0
Copy ctor Book  @0x9c0510
Copy ctor Ruler     @0x9c0508
Copy ctor Pencil    @0x9c050c
dtor    Ruler   @0x9cd500
dtor    Pencil  @0x9cd504
dtor    Ruler   @0x9c0508
dtor    Pencil  @0x9c050c
dtor    Book    @0x9c0510
dtor    Sharpener   @0x62ff0c
dtor    Notebook    @0x62ff10
dtor    Book    @0x62ff14
dtor    Pencil  @0x62ff18
dtor    Ruler   @0x62ff1c

What is happening after default constructions? why does this ruler create too many copies and destroy them? What is the problem here?

Upvotes: 0

Views: 120

Answers (3)

FamZ
FamZ

Reputation: 481

Basically what is happening is that if a vector is full and you are pushing an element, the vector needs to resize. So when you push Ruler on the vector there's an array with a size of sizeof(items) created and the data is copied in(first copy constructor call for Ruler). Then you are pushing Pencil on the vector(first copy constructor call for Pencil), but the vector is out of memory, so a new array is allocated with double of the previous size 2 * sizeof(items) and the old array is copied to the new array(second copy constructor call for Ruler) and the old array is destroyed(first destructor called for Ruler in old array) and so on...

Upvotes: 1

Dietmar K&#252;hl
Dietmar K&#252;hl

Reputation: 153840

Your "default constructor" takes an argument and is, thus, not a default constructor in the C++ sense: default constructors can be called without any argument.

With respect to your question it seem the sequence is thus:

  1. Initially the objects all get constructed.
  2. There is a copy of the Ruler object inserting it into the school vector.
  3. When Pencil is inserted this object is first copied but apparently there isn't enough space in the school vector: the Pencil object is copied into a newly allocated location and then Ruler is copied over and the original space is destroyed.
  4. When Book is inserted the same sequence happens: the Book is copied into a new location and then Ruler and Pencil are copied there, too.
  5. Finally, all the objects get destroyed.

The sequence is somewhat confusing in the sense that std::vector normally doesn't behave like that: it normally creates a small array right at the start which can accomodate more than one element. It seems, the implementation used starts off with a capacity() of just one and then doubles the capacity from there. You can verify this behavior by looking at school.capacity() between the calls to school.push_back(): whenever the capacity() increases, the underlying array gets increased.

Considering that your are allocating memory you may want to consider a move constructor which transfers the stored memory rather than allocating a new array to accomodate a copy to then just delete the original. The move constructor would look something like this:

items::items(items&& other)
    : name(other.name) {
    this->name = nullptr;
}

Upvotes: 2

Lilit Zakaryan
Lilit Zakaryan

Reputation: 89

std::vector manages its own memory. That means that std::vector only stores copies of an object, which means the object must have a meaningful copy constructor (and an assignment operator, but that's another issue).

When the destructor of a vector is invoked the memory held by the vector is released. std::vector also invokes an object's destructor when it is removed (through erase, pop_back, clear or the vector's destructor).

Upvotes: 1

Related Questions