2009-06-08 16 views
5

Me estoy confundiendo mucho con la gestión de memoria en relación con los vectores y podría hacer con algunos conceptos básicos que explican.C++ gestión de la memoria y vectores

Tengo un programa que usa vectores grandes. creé los vectores con el nuevo operadory liberarlos al final del programa con eliminar para obtener la memoria de nuevo.

Mi pregunta es, si el programa se bloquea o se aborta por cualquier motivo, el eliminar líneas se puede perder, hay una manera de recuperar la memoria incluso en este escenario.

que también tienen algunos otros grandes vectores que asigno sin la nueva palabra clave. He leído que estos se crearán en el montón, pero no es necesario desasignarlos de todos modos ya que la gestión de la memoria se trata 'bajo el capó'. Sin embargo, no estoy seguro de que este sea el caso, ya que cada vez que ejecuto mi programa, pierdo RAM.

Así que mi segunda pregunta es, pueden vectores creados sin la nueva palabra clave realmente ser dejados a su suerte y de confianza para limpiar lo que ensucian incluso si el código es abortado mediados de flujo.

Y supongo que una tercera pregunta que se acaba surgido a la mente es, si los vectores se crean automáticamente en el montón ¿por qué se ha consumido alguna vez la nueva palabra clave con ellos? Gracias por leer, ben

+2

Por "cada vez que ejecuto mi programa, pierdo RAM", quiere decir "mi RAM disponible se vuelve más pequeña hasta que salgo del programa" o "mi RAM disponible se hace más pequeña incluso después de salir del programa, y ​​se vuelve aún más pequeña la próxima vez que corro, hasta que un día no me quede absolutamente nada de RAM "? –

+0

Estoy secundando la pregunta de Max. Windows, en mi opinión, no descarga programas finalizados a menos que sea necesario. De esa manera, comienzan a funcionar más rápido después de la primera vez. –

+0

"Y supongo que una tercera pregunta que acaba de surgir es si los vectores se crean automáticamente en el montón, ¿por qué alguna vez usarías la palabra clave nueva con ellos?" Solo necesitarás hacer esto si necesitas pasar el vector a un punto fuera del alcance actual. Esto es relativamente raro en la práctica. – rlbond

Respuesta

13

Sospecho que sus preguntas son sobre std :: vector < T> (a diferencia de una matriz T []).

  1. Cuando su aplicación se cuelga o se interrumpe por alguna razón, el sistema operativo recupera la memoria. Si no, estás usando un sistema operativo verdaderamente raro y has descubierto un error.
  2. Debe distinguir entre la memoria utilizada por el vector y la memoria de sus objetos contenidos. El vector se puede crear en el montón o en la pila, como anotó, la memoria que asigna para sus elementos contenidos siempre está en el montón (a menos que proporcione su propio asignador que haga algo más). La memoria asignada por el vector se gestiona mediante la implementación del vector, y si el vector se destruye (ya sea porque sale del alcance de un vector en la pila o porque se elimina un vector en el montón) su destructor se asegura de que todo la memoria es liberada
+1

La mayoría de los sistemas operativos integrados y muchos RTOS no limpian los recursos cuando un proceso falla. –

3

Cualquier memoria creada por su programa se liberará cuando se cierre. Esa es una característica del sistema operativo, nada que ver con el lenguaje de programación que está utilizando.

"cada vez que ejecuto mi programa I loose RAM" debe ser debido a algún otro efecto, ¿cómo lo está midiendo?

cuanto a por qué tendría que utilizar "nuevo" - dos razones:

  • Usted desea controlar cuando son liberados
  • ¿Quieres que persisten después se cierra la función actual.
+1

Mismo comentario dado a Tobias.La mayoría de los sistemas operativos integrados y muchos RTOS no limpian los recursos cuando un proceso falla. –

+0

+1 para señalar lo que muchos no se dan cuenta. Un administrador de memoria virtual es un lujo que a menudo se da por sentado. – Void

3

Supongo que hablas sobre std :: vector y no sobre arreglos de lenguaje.

  1. Cuando un programa se bloquea, el sistema operativo recupera su memoria
  2. comunicados std :: vector de la memoria que se asigna. Si está almacenando punteros, no serán eliminados.
  3. Los vectores se crean como cualquier otra variable, no están en el montón solo porque son vectores.
4

Sí, puede confiar en los vectores para limpiarlos después de ellos.

SIN EMBARGO No puede confiar en las cosas que el vector retiene para la limpieza. Lo que debe limpiarse podría ser algo que persista fuera de su aplicación. Si es su memoria, esto no es una preocupación. Si se está asegurando de que las etiquetas XML estén todas cerradas, entonces el sistema operativo no podrá ayudarte.

Por ejemplo, ¿qué pasa si usted tiene un vector de algún objeto de bloqueo poco firme como esto:

class CLock 
    { 
    public: 
     CLock() {} 
     ~CLock() {} 

     void Lock(...) {...} 

     void Unlock(...) {...} 
    }; 

    std::vector<CLock> myLockVec; 

¿Cómo su vector de conocimientos del reloj para desbloquear todo cuando su hecho? Los vectores no están diseñados para saber sobre bloqueos.

Esta es esencialmente la misma situación que tiene un vector de punteros:

std::vector<int*> myIntVec; 

¿Cómo sabe el vector que aquí punteros se han eliminado y NULL'd, y cuáles son realmente allí? Tal vez algunos han sido eliminados y configurados con su valor especial 0xdeadbeef, es decir, eliminados.

El punto es que el vector no tiene medios para saber esto o saber que sus elementos son punteros o bloqueos o lo que sea. Solo necesitan ser elementos que tengan constructores por defecto y que puedan copiarse, y que cumplan con los demás requisitos que el vector tiene en sus elementos.

La solución es estar seguro de que cualquier vector HOLDS debe ser responsable de su limpieza. Esto se llama RAII - La asignación de recursos es la inicialización, más importante aquí, la destrucción de recursos es desasignación. Con nuestro ejemplo de CLock anterior, la respuesta es obvia, ¡asegúrese de desbloquear cuando hayamos terminado!

class CLock 
{ 
     ... 
     ~Clock() 
     { 
      if (locked) 
      { 
       Unlock(); 
      } 
     } 
} 

Pero con los punteros no es tan obvio. La solución es envolver el puntero en una clase smart_ptr. Los más prolíficos de estos son el boost family of smart poniters.

class CSmartPointer<T> 
{ 
     CSmartPointer(T* rawPtr) 
     { 
     m_ptr = rawPtr; 
     } 

     ~CSmartPointer() 
     { 
     delete m_ptr; 
     } 
} 

Las características adicionales se ponen en juego con los punteros, como el recuento de referencias, pero el ejemplo anterior deben darle la esencia de la naturaleza del problema y cómo su típicamente resueltos.

1

Para la "memoria perdida", lo que dice @RichieHindie.

Para la segunda pregunta:

pueden vectores creados sin la nueva palabra clave realmente pueden dejar a sus propios dispositivos y de confianza para limpiar lo que ensucian incluso si el código se aborta mediados flujo

Mientras que la finalización normal del programa (incluida la terminación por excepción) asegura que los destructores se ejecuten (con algunas sutilezas con respecto a los datos estáticos, en teoría, esos deberían ejecutarse también, en la práctica ocasionalmente podría tener problemas), un bloqueo suficientemente fuerte del p rocess no puede garantizar ningún comportamiento, por ejemplo, kill -9 es garantizado para finalizar su programa lo antes posible, sin darle la oportunidad de ejecutar ningún destructor o cualquier otra cosa.

+0

Las excepciones no controladas (aquellas que hacen que el tiempo de ejecución tarde o temprano llame a terminate()) pueden no causar la ejecución de destructores. El comportamiento de desenrollado de la pila en este caso está definido por la implementación. – ASk

8

No utilice new para crear vectores. Solo ponlos en la pila.

El destructor del vector invoca automáticamente el destructor de cada elemento en el vector. Por lo tanto, no tiene que preocuparse por eliminar los objetos usted mismo. Sin embargo, si tiene un vector de punteros, los objetos a los que se refieren los punteros serán no limpios. Aquí hay un código de ejemplo. Para mayor claridad, estoy omitiendo la mayoría de los detalles:

class HeapInt 
{ 
    public: 
     HeapInt(int i) {ptr = new int(i);} 
     ~HeapInt() {delete ptr;} 
     int& get() {return *ptr;} 
    private: 
     int* ptr; 
}; 

int main() 
{ 
    // this code DOES NOT leak memory 
    std::vector<HeapInt> vec; 
    for (int i = 0; i < 10; ++i) 
    { 
     HeapInt h(i); 
     vec.push_back(h); 
    } 
    return 0; 
} 

Aunque main() arroja una excepción, no se pierde memoria. Sin embargo, este código hace Pérdida de memoria:

int main() 
{ 
    // this code though, DOES leak memory 
    std::vector<int*> vec; 
    for (int i = 0; i < 10; ++i) 
    { 
     int* ptr = new int(i); 
     vec.push_back(ptr); 
    } 
    // memory leak: we manually invoked new but did not manually invoke delete 
    return 0; 
} 
+0

Eso no siempre funciona. –

+4

Sí, pero la persona que hace la pregunta claramente no sabe que los vectores deberían colocarse en la pila. – rlbond

+0

¿Cómo podría el segundo ejemplo tener una pérdida de memoria? el programa termina y el sistema operativo recupera la memoria, o me está perdiendo algo terriblemente importante sobre la gestión de la memoria –

2

Uno de los dos es un poco confundido aquí.

Si está utilizando std :: vector, no necesita asignar memoria manualmente para sus elementos. El espacio extra se asignará automáticamente cuando sea necesario cada vez que realice un push_back(). Si necesita todo el espacio preasignado por algún motivo, puede llamar a reserva(). De cualquier manera, la memoria se libera automágicamente cuando el vector se destruye.

Si está haciendo std :: vector nuevo, obtendrá un puntero al vector. Eso no es diferente a llamar a alguien nuevo en cualquier otra clase. Usted crea un puntero a un objeto de esa clase y se destruirá cuando llame a delete. Si no te gusta ese comportamiento, intenta crear tu vector en la pila.

1

Otro escenario que no se menciona con respecto a cuándo se desea usar "nuevo", es en algunos casos cuando un vector es una variable miembro de una clase. El NULL se puede usar como un semáforo adicional, por ejemplo, durante la creación a pedido; Además, si el uso del vector está escasamente poblado en su clase, entonces ni siquiera crear uno a menos que sea realmente necesario le ahorrará memoria, a expensas de la penalización adicional de 4 bytes en todas las instancias, así como la penalización en tiempo de ejecución de la indirección del puntero.

Cuestiones relacionadas