2009-10-19 32 views
14

Me encuentro muchas veces con el código donde std :: vector :: clear() del miembro de clase del tipo std :: vector se llama en constructor y destructor.std :: vector :: clear() en constructor y destructor

No veo por qué se requiere:

  1. constructor - el miembro de la clase de tipo std :: vector está vacío por defecto, por lo que no hay necesidad de llamar a clear().
  2. destructor - El miembro de clase del tipo std :: vector se destruirá como parte de la destrucción estándar del objeto que lo contiene. Como parte de la destrucción del vector, todos los objetos de valor que contengan se destruirán (si montó punteros asignados a la memoria, deberían eliminarse "manualmente"), así que de nuevo no es necesario llamar a clear().

¿Echo de menos algo?

Respuesta

16

Del sonido de las cosas, las personas que escribieron ese código fueron las que se perdieron algo. La única vez que tendría sentido llamar a clear() en un ctor o dtor estaría en el medio de algún otro código. Por ejemplo, un controlador puede leer algunos datos, procesarlos y luego leer más datos. En tal caso, probablemente sea más rápido usar un solo contenedor para los datos a medida que los lee, y borrarlos cada vez, que crear un nuevo contenedor cada iteración.

5

No, tienes razón. A menos que haya algún negocio adicional en el constructor (o el constructor de la clase base) que requieren que, pero las posibilidades son muy bajos ...

Más tarde editar

En caso de destructor, una de las Los errores más comunes que vi es que algunas personas suponen que el método claro también llamará a eliminar para vectores de punteros (vector), que, por supuesto, no es el caso

22

No, no te falta nada. Sospecho que esto es (inofensivo) programación vudú, algo así como establecer un puntero a nulo después de liberarlo, o llamar aleatoriamente a volver a pintar/revalidar en código GUI. El programador recuerda que ayudó con algún tipo de error en el pasado y ahora lo agrega innecesariamente "por las dudas". Quién sabe, quizás ayude. Vudú.

+2

nice term, 'voodoo programming' :) – xtofl

+7

Establecer un puntero a null no es vudú, ayuda mucho en la depuración. También evita que elimines el mismo puntero dos veces, pero nadie lo hace, ¿verdad? –

+12

En todo caso, desea saber que ha eliminado el mismo puntero dos veces. Es mejor fallar ruidosamente (y descubrirlo) que tener éxito accidentalmente (enmascarar un error que volverá a morderlo) –

7
  1. Es TOTALMENTE unnessecary para borrar el contenido de un contenedor STL en un constructor
  2. Es unnessecary para borrar el contenido de un contenedor STL en un destructor MENOS el recipiente contiene un puntero. Si el puntero se ha creado utilizando new, aún necesita ser borrado primero. Después de eso, todavía no será necesario limpiar el contenedor.

Considera:

#define BOOST_TEST_MODULE StlContainers 
#define BOOST_LIB_DIAGNOSTIC 

#include <boost/test/unit_test.hpp> 
#include <boost/assign.hpp> 
#include <boost/assign/list_of.hpp> 
#include <boost/assign/std/vector.hpp> 

#include <vector> 

using namespace boost::assign; 
using namespace std; 

const vector<int> my_ints_vector = list_of(0)(1)(1)(2)(3)(5)(8)(13)(21)(34); 

struct ScopedStruct1 
{ 
     ScopedStruct1(const vector<int> & v) : m_v(v) {} 
     ~ScopedStruct1() {} 
    private : 
     vector<int> m_v; 
}; 

class A 
{ 
    public : 
     A(int i) : m_i(i) {} 
     ~A() {} 
    private : 
     int m_i; 
}; 

struct ScopedStruct2 
{ 
    ScopedStruct2() {} 
    ~ScopedStruct2() { for(vector<A*>::iterator it = m_v.begin(); it != m_v.end(); ++it) delete *it; } 

    vector<A*> m_v; 
}; 

struct ScopedStruct3 
{ 
    ScopedStruct3() {} 
    ~ScopedStruct3() { /* no deletion */ } 

    vector<A*> m_v; 
}; 

BOOST_AUTO_TEST_CASE(StlContainer_storing_something_simple) 
{ 
    ScopedStruct1 str(my_ints_vector); 
} 

BOOST_AUTO_TEST_CASE(StlContainer_storing_pointers_with_delete) 
{ 
    ScopedStruct2 str; 
    for(int i = 0; i < 10; i++) 
     str.m_v.push_back(new A(i)); 
} 

BOOST_AUTO_TEST_CASE(StlContainer_storing_pointers_without_delete) 
{ 
    ScopedStruct3 str; 
    for(int i = 0; i < 10; i++) 
     str.m_v.push_back(new A(i)); 
} 

Usando marco unit_test de impulso He creado 3 casos de prueba. El framework unit_test es mejor porque rastrea fugas de memoria. Observará que el primer y el segundo caso de prueba no generan pérdidas de memoria, pero el tercer caso sí lo hace porque los contenidos del vector no se borran.

+0

la única respuesta con código de ejemplo ... ¡agradable! – nkint

-4

Por supuesto, hay que llamar a clear() o cambiar el tamaño (0) o la palabra equivalente (std :: _ Destroy_range (...) en el destructor antes de desasignar

desasignación se realiza a través de asignador.: : desasignar el que no se ejecuta ningún destructores simplemente libera la memoria

claro() es equivalente a cambiar el tamaño (0) que se extiende destructores en el primer tamaño() cosas en el búfer asignado

nO.. solo se asignaron punteros, manejadores de archivos , mantiene mutexes, todos los demás recursos recuperables en poder del objeto. Los destructores DEBEN ejecutarse. Antes de la creación de instancias, la plantilla no sabe que el destructor es trivial. Si el destructor es trivial, ENTONCES se optimiza DESPUÉS de la instanciación

+1

No, los destructores son definitivamente llamados cuando un vector sale del alcance. No llamar destructores no tiene sentido. Le sugiero que examine cuidadosamente su implementación.Puede probar esto ejecutando algo como esto: # include # include struct {~ Prueba Test() {std :: cout << "Adiós, mundo \ n"; }}; int main() {std :: cout << "Crear \ n"; std :: vector t (1); std :: cout << "Creado \ n"; } – janm

+5

Absolutamente incorrecto. –

+0

Lo leí discutiendo una llamada clara en ~ vector() en lugar de la discusión de una llamada clara sobre una variable de instancia de miembro vectorial de una clase definida por el usuario - mi error de lectura. – pgast

1

El único caso que puedo pensar en dónde sería útil es cuando el orden de destrucción importa y el destructor quiere asegurarse de que los objetos en el vector se destruyan antes que otra cosa.

Por supuesto, es mejor estructurar el código para no requerir eso; sin embargo, es una razón concebible.

1

A pesar de lo que se ha dicho hasta ahora, hay al menos un escenario cuando una llamada explícita a clear en el destructor podría ser necesaria.

Imagine la situación cuando el objeto que se destruye tiene varios objetos subobjetos miembros, que requieren un orden de destrucción específico, es decir, los subobjetos dependen uno del otro y un orden incorrecto de destrucción provocará resultados no deseados. Como probablemente sepa, el orden de destrucción del subobjeto del miembro (así como la inicialización del miembro) está determinado por el orden de la declaración de los miembros en la definición de la clase. Por lo tanto, una forma de lograr el orden correcto de destrucción es organizar las declaraciones de los miembros en consecuencia. Sin embargo, en primer lugar, esta no es una solución muy buena para el mantenimiento. En segundo lugar, el orden deseado de destrucción podría depender de algunas condiciones de tiempo de ejecución. En tercer lugar, el orden deseado de destrucción podría contradecir el orden de inicialización deseado. Todo esto significa que podría no ser posible (o sabio) ordenar el orden apropiado de destrucción mediante la reorganización de las declaraciones.

Un enfoque razonable en este caso podría ser la limpieza manual de algunos subobjetos de miembros críticos, llamando a sus métodos clean o similares, hasta que la dependencia de la orden de destrucción "desaparezca". Supongo que quizás el código que viste estaba tratando de resolver el problema de pedidos llamando al clean en un subobjeto vector estratégicamente seleccionado.

En cuanto a llamar al clean en el constructor ... No tengo idea de por qué alguien haría algo así.

+0

Parece que vives en una palabra demasiado realista :) Cada vez que examiné el código, trató de lograr "limpiar" en constructor/destructor. En cuanto a la situación que describes, difícilmente puedo amalarte. Lo mejor se debe hacer para evitarlo y, si no fuera posible, debería ser bien comentado – dimba

+0

Podría estar equivocado, pero cuando el objeto stl está muriendo al final del alcance, por ejemplo. Llama al destructor del objeto dentro. Si dicho objeto también es un contenedor STL, también debe limpiarse automáticamente. Una situación excepcional surgiría con un puntero iniciado por "nuevo" - como de costumbre – Maciek

+1

@Maciek: Nadie discute eso. Lo que estoy diciendo, una vez más, es que el orden de esa limpieza * automática * podría ser inaceptable en algunos casos. En esos casos, es posible que tenga que hacer su propia limpieza previa ordenada por encargo antes de que comience la limpieza automática. – AnT

Cuestiones relacionadas