#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;
}

Tengo resultados extraños. ¿Puedes explicar lo que sucede detrás de escena? Resultados:

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

¿Qué sucede después de las construcciones predeterminadas? ¿Por qué esta regla crea demasiadas copias y las destruye? ¿Cuál es el problema aquí?

0
Rıfat Tolga Kiran 25 dic. 2016 a las 23:54

3 respuestas

La mejor respuesta

Básicamente, lo que está sucediendo es que si un vector está lleno y está presionando un elemento, el vector debe cambiar su tamaño. Entonces, cuando presiona Ruler en el vector, se crea una matriz con un tamaño de sizeof(items) y los datos se copian (la primera llamada del constructor de copia para Ruler). Luego, está presionando Pencil en el vector (llamada del constructor de la primera copia para Pencil), pero el vector no tiene memoria, por lo que se asigna una nueva matriz con el doble del tamaño anterior 2 * sizeof(items) y la matriz anterior se copia en la nueva matriz (llamada del constructor de la segunda copia para Ruler) y la matriz antigua se destruye (el primer destructor requiere Ruler en la matriz antigua) y así sucesivamente ...

1
FamZ 25 dic. 2016 a las 23:18

Su "constructor predeterminado" toma un argumento y, por lo tanto, no es un constructor predeterminado en el sentido de C ++: se puede llamar a los constructores predeterminados sin ningún argumento.

Con respecto a su pregunta, parece que la secuencia es así:

  1. Inicialmente, todos los objetos se construyen.
  2. Hay una copia del objeto Ruler insertándolo en el vector school.
  3. Cuando se inserta Pencil, este objeto se copia primero pero aparentemente no hay suficiente espacio en el vector school: el objeto Pencil se copia en una ubicación recién asignada y luego {{X3} } se copia y se destruye el espacio original.
  4. Cuando se inserta Book, ocurre la misma secuencia: el Book se copia en una nueva ubicación y luego también se copian allí Ruler y Pencil.
  5. Finalmente, todos los objetos se destruyen.

La secuencia es algo confusa en el sentido de que std::vector normalmente no se comporta así: normalmente crea una pequeña matriz justo al comienzo que puede acomodar más de un elemento. Parece que la implementación utilizada comienza con un capacity() de solo uno y luego duplica la capacidad desde allí. Puede verificar este comportamiento mirando school.capacity() entre las llamadas a school.push_back(): cada vez que aumenta capacity(), aumenta la matriz subyacente.

Teniendo en cuenta que está asignando memoria, es posible que desee considerar un constructor de movimiento que transfiera la memoria almacenada en lugar de asignar una nueva matriz para acomodar una copia y luego simplemente delete el original. El constructor de movimiento se vería así:

items::items(items&& other)
    : name(other.name) {
    this->name = nullptr;
}
2
Dietmar Kühl 25 dic. 2016 a las 21:24

Std :: vector gestiona su propia memoria. Eso significa que std :: vector solo almacena copias de un objeto, lo que significa que el objeto debe tener un constructor de copia significativo (y un operador de asignación, pero ese es otro problema).

Cuando se invoca el destructor de un vector, se libera la memoria que posee el vector. std :: vector también invoca el destructor de un objeto cuando se elimina (mediante erase, pop_back, clear o el destructor del vector).

1
Lilit Zakaryan 25 dic. 2016 a las 21:09