2008-09-19 8 views

Respuesta

53

Utilice la pila cuando su variable no se utilizará después de que la función actual regrese. Use el montón cuando los datos en la variable se necesiten más allá de la duración de la función actual.

+3

Sin embargo, hay formas de evitarlo. Pasar un búfer a una función que luego le escribe datos es una buena manera de que una función "devuelva" datos dinámicos que permanecen en un marco de pila inferior. Es menos parecido a OO, pero es mucho más eficiente. –

+6

El tamaño también es una consideración: todo lo que supere 1K en la pila debe considerarse cuidadosamente. A veces es mejor tener un puntero de pila para acumular memoria (junto con la expresión 'Adquisición de recursos es inicialización') – Arkadiy

+1

Pero, ¿qué ocurre cuando la memoria es propiedad de una clase? ¿Cómo se decide cuándo una propiedad de clase debe ser un puntero o no? ¿También cuándo podría usar un puntero inteligente? – JagWire

3

Utilice el montón para asignar espacio únicamente a los objetos en tiempo de ejecución. Si conoce el tamaño en tiempo de compilación, use la pila. En lugar de devolver los objetos asignados por el montón de una función, pase un búfer a la función para que escriba. De esta forma, el búfer puede asignarse donde se llama a la función como una matriz u otra estructura basada en la pila.

Cuantas menos declaraciones malloc() tenga, menores serán las posibilidades de pérdida de memoria.

0

La pregunta está mal formada.

Hay situaciones en las que necesita la pila, otras donde necesita el montón, otras donde necesita el almacenamiento estático, otras donde necesita los datos de memoria constante, otras donde necesita la tienda gratuita.

La pila es rápida, porque la asignación es solo un "incremento" sobre el SP, y toda la "asignación" se realiza en el momento de invocación de la función en la que se encuentra. La asignación/desasignación del montón es más tiempo caro y propenso a errores.

28

Como regla general, evite crear objetos grandes en la pila.

  • Crear un objeto en la pila le libera de la carga de recordar limpiar (leer borrar) el objeto. Pero crear demasiados objetos en la pila aumentará las posibilidades de desbordamiento de pila.
  • Si usa Heap para el objeto, obtiene la mayor cantidad de memoria que el sistema operativo puede proporcionar, mucho más grande que la pila, pero nuevamente debe asegurarse de liberar la memoria cuando haya terminado. Además, crear demasiados objetos con demasiada frecuencia en el montón tenderá a fragmentar la memoria, lo que a su vez afectará el rendimiento de la aplicación.
12

Utilice la pila cuando la memoria utilizada esté estrictamente limitada al ámbito en que la está creando. Esto es útil para evitar fugas de memoria porque sabe exactamente dónde desea usar la memoria y sabe cuándo ya no la necesita, por lo que la memoria se limpiará por usted.

int main() 
{ 
    if (...) 
    { 
     int i = 0; 
    } 
    // I know that i is no longer needed here, so declaring i in the above block 
    // limits the scope appropriately 
} 

El montón, sin embargo, es útil cuando la memoria se puede acceder fuera del alcance de su creación y no se desea copiar una variable de pila. Esto puede darle control explícito sobre cómo se asigna y desasigna la memoria.

Object* CreateObject(); 

int main() 
{ 
    Object* obj = CreateObject(); 
    // I can continue to manipulate object and I decide when I'm done with it 

    // .. 
    // I'm done 
    delete obj; 
    // .. keep going if you wish 
    return 0; 
} 

Object* CreateObject() 
{ 
    Object* returnValue = new Object(); 
    // ... do a bunch of stuff to returnValue 
    return returnValue; 
    // Note the object created via new here doesn't go away, its passed back using 
    // a pointer 
} 

Obviamente, un problema común aquí es que puede olvidarse de eliminar su objeto. Esto se llama pérdida de memoria. Estos problemas son más frecuentes a medida que su programa se vuelve cada vez menos trivial, donde la "propiedad" (o quién es exactamente el responsable de borrar las cosas) se vuelve más difícil de definir.

Las soluciones comunes en los lenguajes más administrados (C#, Java) son implementar la recolección de basura para que no tenga que pensar en eliminar cosas.Sin embargo, esto significa que hay algo en el fondo que se ejecuta de forma aperiódica para verificar los datos de tu montón. En un programa no trivial, esto puede volverse bastante ineficaz a medida que aparece un hilo de "recolección de basura" y se escapa, buscando datos que deberían eliminarse, mientras que el resto de su programa está bloqueado para ejecutarse.

En C++, la solución más común y la mejor (en mi opinión) para hacer frente a fugas de memoria es utilizar un puntero inteligente. La más común de ellas es que es boost::shared_ptr (reference counted)

Así que para recrear el ejemplo anterior CreateObject impulso :: shared_ptr();

int main() 
{ 
    boost::shared_ptr<Object> obj = CreateObject(); 
    // I can continue to manipulate object and I decide when I'm done with it 

    // .. 
    // I'm done, manually delete 
    obj.reset(NULL); 
    // .. keep going if you wish 
    // here, if you forget to delete obj, the shared_ptr's destructor will note 
    // that if no other shared_ptr's point to this memory 
    // it will automatically get deleted. 
    return 0; 
} 

boost::shared_ptr<Object> CreateObject() 
{ 
    boost::shared_ptr<Object> returnValue(new Object()); 
    // ... do a bunch of stuff to returnValue 
    return returnValue; 
    // Note the object created via new here doesn't go away, its passed back to 
    // the receiving shared_ptr, shared_ptr knows that another reference exists 
    // to this memory, so it shouldn't delete the memory 
} 
+0

Y luego hubo semántica de movimiento –

4

como regla general, utilice la pila cada vez que pueda. es decir, cuando la variable nunca se necesita fuera de ese alcance.

es más rápido, causa menos fragmentación y va a evitar los otros gastos generales asociados con la llamada malloc o nueva. La asignación fuera de la pila es un par de operaciones de ensamblador, malloc o nuevo es varios cientos de líneas de código en una implementación eficiente.

Nunca es mejor usar el montón ... solo inevitable. :)

+0

Es mejor que un par de operaciones de ensamblador: es solo un agregado o resta (dependiendo de en qué dirección crezca su pila). – Eclipse

+0

agrega y resta no siempre son operaciones individuales ... pero también considera la limpieza en el otro extremo. Dependiendo de la convención de llamadas, habrá un sub/agregar para que coincida con el add/sub, aunque todos pueden combinarse dependiendo de cómo se usa exactamente la pila y qué optimizaciones hace el compilador (en realidad puede reducirse a cero instrucciones. .. o en casos muy especiales, menos las instrucciones) – jheriko

8

Una excepción a la regla mencionada anteriormente que por lo general debe utilizar la pila para las variables locales que no son necesarios fuera del ámbito de la función:

funciones recursivas pueden agotar el espacio de pila si se asignan local grande variables o si se invocan recursivamente muchas veces. Si tiene una función recursiva que utiliza memoria, puede ser una buena idea usar memoria basada en pila en lugar de memoria basada en pila.

Cuestiones relacionadas