2012-02-17 9 views
13

Parece que cada vez que agrego un objeto al vector m_test, se llama al método destructor. ¿Me estoy perdiendo de algo? ¿Cómo puedo evitar que esto suceda?¿Por qué se llama el destructor de mi clase cuando agrego instancias a un vector?

class TEST 
{ 
public: 
    TEST(); 
    ~TEST(); 
    int * x; 
}; 

TEST::TEST() 
{ 
} 

TEST::~TEST() 
{ 
... it is called every time I push_back something to the vector ... 
    delete x; 
} 

    vector<TEST> m_test; 
    for (unsigned int i=0; i<5; i++) 
    { 
     m_test.push_back(TEST()); 
    } 
+2

En C++ 11, puede usar 'm_test.emplace_back()' para evitar crear el temporal. En cualquier caso, siempre recuerde la [Regla de Tres] (http://stackoverflow.com/questions/4172722). –

Respuesta

9

El problema aquí es que usted está violando la Rule of Three. Su clase tiene un destructor, por lo que necesita un constructor de copias y un operador de asignación. Alternativamente, no puede permitir que se copie su clase (por ejemplo, haciendo T(T const&) y T& operator=(T const&) en privado, o derivando de boost::noncopyable), y luego cambie el tamaño del vector en lugar de usar push_back.

En el primer caso, puede simplemente push_back su clase como lo haría normalmente.En la segunda, la sintaxis sería algo así como

std::vector<TEST> vec(5); 
// vec now has five default-constructed elements of type TEST. 

No hacer ninguna de estas cosas es una mala idea, ya que es muy probable que tenga problemas de doble deleción en algún momento - incluso si usted piensa que' ll nunca copiar o asignar un TEST donde x != nullptr, es mucho más seguro prohibirlo explícitamente.

Por cierto, si usted tiene punteros miembros que se deben eliminar cuando un objeto sale del ámbito, considerar el uso de punteros inteligentes como scoped_ptr, unique_ptr y shared_ptr (y tal vez auto_ptr si no es capaz de usar Boost o C + +11).

+0

Gracias Anton, soy nuevo en la programación de C++, y nunca he oído hablar de Rule of Three. Gracias por indicarme la dirección correcta. – 2607

7

No se llama cuando push_back, se llama cuando el temporal se destruye.

solucionarlo en su ejemplo:

TEST test; 
for (int i = 0; i < 5; ++i) 
{ 
    m_test.push_back(test); 
} 

sólo debe llamar una vez.

Su código está creando un TEST temporal dentro del ciclo, usándolo en push_back, y luego ese temporal se sale del alcance cuando el ciclo termina/repite y se destruye. Eso ocurre exactamente como debería, ya que el TEST temporal necesita limpiarse.

Si desea evitar eso, debe hacer cualquier otra cosa que no sea crear un objeto temporal para cada inserción. Una posible solución es:

vector<TEST> m_test(5); // Note reserving space in the vector for 5 objects 

std::fill(m_test.begin(), m_test.end(), TEST()); // Fill the vector with the default ctor 

Dependiendo de como se optimiza la STL, esto puede no necesitar hacer varias copias.

También puede ser capaz de obtener un mejor manejo si se implementa un constructor de copia en su clase TEST, como:

TEST::TEST(const TEST & other) 
{ 
    x = new int(*other.x); // Not entirely safe, but the simplest copy ctor for this example. 
} 

si esto es apropiado, o cómo manejar la situación, depende de su clase y su necesita, pero normalmente debe tener un constructor de copia cuando haya definido su propio constructor y destructor (de lo contrario, el compilador generará uno, y en este caso, dará como resultado punteros copiados y colgantes al x).

1

vector.push_back() copia el objeto dado en su área de almacenamiento. El objeto temporal que está construyendo en la llamada push_back() se destruye inmediatamente después de ser copiado, y eso es lo que está viendo. Algunos compiladores pueden optimizar esta copia, pero aparentemente los tuyos no pueden.

1

En m_test.push_back(TEST());, TEST() creará una variable temporal. Después de que el vector lo copie en su propia memoria, la variable temporal se destruye.

puede hacerlo de esta manera:

vector<TEST> m_test(5, TEST()); 
0

para evitar la destrucción de un temporal y para evitar constructores de copia, considere el uso vector::resize o vector::emplace_back. Aquí hay un ejemplo usando emplace_back:

vector<TEST> m_test; 
m_test.reserve(5); 
for (uint i=0; i<5; i++) 
{ 
    m_test.emplace_back(); 
} 

El elemento del vector se construirá en el lugar sin la necesidad de copiar. Cuando se destruye vt, cada elemento vectorial se destruye automáticamente.

C++ 0x es necesario (use -std=c++0x con gnu). #include <vector> es, por supuesto, también obligatorio.

Si un constructor por defecto no se utiliza (por ejemplo, si el TEST::x era una referencia en lugar de un puntero), sólo tiene que añadir me lo quitaron a la llamada a emplace_back() de la siguiente manera:

class TEST 
{ 
public: 
    TEST(int & arg) : x(arg) {;} // no default constructor 
    int & x; // reference instead of a pointer. 
}; 

. . . 

int someInt; 

vector<TEST> m_test; 
m_test.reserve(5); 
for (uint i=0; i<5; i++) { 
    m_test.emplace_back(someInt); // TEST constructor args added here. 
} 

El reserve() se muestra es opcional pero asegura que hay suficiente espacio disponible antes de comenzar a construir elementos vectoriales.

Cuestiones relacionadas