practices guidelines best c++ stl

guidelines - c++ best practices



std:: vector:: clear() en constructor y destructor (7)

  1. Es totalmente innecesario limpiar el contenido de un contenedor de STL en un constructor
  2. Es innecesario borrar el contenido de un contenedor de STL en un destructor A MENOS QUE el contenedor contenga un puntero. Si el puntero se ha creado utilizando nuevo , aún debe eliminarse primero. Después de eso todavía no será necesario vaciar el contenedor.

Considera esto :

#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 el framework unit_test de boost, he creado 3 casos de prueba. El marco unit_test es muy grande porque rastrea las fugas de memoria. Notará que el primer y el segundo caso de prueba no generan pérdidas de memoria, pero el tercer caso si los contenidos del vector no se eliminan.

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

No veo por qué se requiere:

  1. constructor : el miembro de clase de tipo std :: vector está vacío de forma predeterminada, por lo que no es necesario llamar a clear ().
  2. destructor : el miembro de la clase de tipo std :: vector se destruirá como parte de la destrucción estándar del objeto que lo contiene. Como parte de la destrucción vectorial, todos los objetos de valor contenidos en ella serán destruidos (si acumula punteros asignados a la memoria, deben eliminarse "manualmente"), así que nuevamente no es necesario llamar a clear ().

¿Echo de menos algo?


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

Imagine que la situación en la que el objeto que se destruye tiene varios subobjetos miembros, que requieren un orden específico de destrucción, es decir, los subobjetos dependen de alguna manera entre sí, y un orden incorrecto de su destrucción conducirá a resultados no deseados. Como probablemente sepa, el orden de destrucción de subobjetos de miembros (así como la inicialización de miembros) está determinado por el orden de declaración de los miembros en la definición de clase. Por lo tanto, una forma de lograr el orden de destrucción adecuado 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 de destrucción deseado podría depender de algunas condiciones de tiempo de ejecución. En tercer lugar, el orden de destrucción deseado podría contradecir el origen deseado de la inicialización. Todo esto significa que podría no ser posible (o sabio) ordenar el orden correcto de destrucción al reorganizar las declaraciones.

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

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


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 sería en medio de algún otro código. Por ejemplo, un ctor puede leer algunos datos, procesarlos y luego leer más datos. En tal caso, es probable que sea más rápido usar un solo contenedor para los datos a medida que los lee, y borrarlos cada vez, que crear un contenedor nuevo cada iteración.


El único caso en el 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.


No, no te estás perdiendo nada. Sospecho que esta es una programación vudú (inofensiva), algo así como establecer un puntero en nulo después de liberarlo, o llamar al azar a repintar / revalidar en el código GUI. El programador recuerda que ayudó con algún tipo de error en el pasado y ahora lo agrega innecesariamente "por si acaso". Quién sabe, tal vez ayude. Vudú.


No, tienes razón. A menos que haya algún negocio adicional en el constructor (o constructor de las clases base) que lo requiera, pero las posibilidades son muy bajas ...

Edición posterior

En el caso de un destructor, uno de los errores más comunes que vi es que algunas personas asumen que el método claro también requerirá la eliminación de vectores de indicadores (vector), lo cual, por supuesto, no es el caso


Por supuesto, uno tiene que llamar a clear () o redimensionar (0) o el equivalente a decir (std :: _ Destroy_range (...) en el destructor antes de desasignar.

La desasignación se realiza a través de allocator :: deallocate que NO ejecuta ningún destructor . Simplemente libera la memoria.

clear () es equivalente a redimensionar (0) que ejecuta destructores en los primeros elementos de tamaño () en el búfer asignado

NO solo punteros asignados, identificadores de archivos, retenciones mutuas, todos los demás recursos recuperables retenidos por el objeto. Los destructores DEBEN ser ejecutados. 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