2012-07-13 12 views
12

Ok, así que estoy muy nuevo en la programación C++, y he estado mirando a su alrededor por un par de días para una respuesta decisiva para esto. ¿CUÁNDO debería declarar las variables miembro en el montón frente a la pila? La mayor parte de las respuestas que he encontrado han abordado otras cuestiones, pero quiero saber cuándo es mejor utilizar el montón de variables miembro y por qué es mejor para amontonar los miembros en lugar de apilarlos.para declarar variables miembro en el montón de C++

+1

* las variables * member * se refieren a los miembros * class * y, como tales, van a donde va la * instancia * de la clase. – CapelliC

Respuesta

18

Hay dos conceptos importantes para captar primero:

  1. uno debe evitar pensar en términos de "montón" y "pila". Esos son detalles de implementación de su compilador/plataforma, no del lenguaje. En su lugar, pensar en términos de vidas objeto: debería vida útil del objeto corresponda a la de su "padre", o debe sobrevivir a ella? Si necesita lo último, necesitará usar new (directa o indirectamente) para asignar dinámicamente un objeto.

  2. Variables miembro siempre tienen la misma vida que su elemento primario. La variable miembro puede ser un puntero y el objeto al que apunta puede tener una vida útil independiente. Pero el objeto apuntado no es una variable miembro.

Sin embargo, no hay una respuesta general a tu pregunta. Crudo hablando, no se asigna dinámicamente a menos que haya una buena razón para hacerlo. Como indiqué anteriormente, estas razones generalmente corresponden a situaciones en las que la duración de la vida debe diferir de su "padre".


1. De hecho, el estándar de C++ en realidad no hablar de "montón" y "pila". Es importante tenerlos en cuenta al optimizar o, en general, pensar en el rendimiento, pero en su mayoría son irrelevantes desde el punto de vista de la funcionalidad del programa.

+0

"generalmente corresponden a situaciones en las que la duración de la vida debe diferir de la de su padre": ¿podemos hablar de algo como miembro entonces? –

+0

@JamesKanze: De hecho, debería aclarar eso. –

+1

Los objetos también requieren tiempo de vida dinámico si tienen un tamaño dinámico o para convertir un objeto en una clase base virtual. –

1

La pila se refiere a call stack. Las llamadas a función, las direcciones de retorno, los parámetros y las variables locales se mantienen en la pila de llamadas. Utiliza memoria de pila cada vez que pasa un parámetro o crea una variable local. La pila solo tiene almacenamiento temporal. Una vez que la función actual está fuera del alcance, ya no tiene acceso a ninguna variable para los parámetros.

El montón es una gran cantidad de memoria utilizada para la asignación dinámica. Cuando utiliza el operador new para asignar memoria, esta memoria se asigna desde el montón. Desea asignar memoria de montón cuando crea objetos que no desea perder después de que la función actual finaliza (pierde alcance). Los objetos se almacenan en el montón hasta que el espacio se desasigna con delete o free().

+0

Los objetos también requieren tiempo de vida dinámico si tienen un tamaño dinámico o para convertir un objeto en una clase base virtual. –

4

variables de ellos son miembros de la clase en sí. No son ni sobre el montón ni en la pila, o más bien, son que cada vez la clase en sí es .

Hay muy pocas razones para agregar un nivel de indirección, y asignar un miembro separado en el montón: el polimorfismo (si el tipo del miembro no es siempre la misma) es con mucho el más común.

+0

Los objetos también requieren tiempo de vida dinámico si tienen un tamaño dinámico o para convertir un objeto en una clase base virtual. –

+0

@MooingDuck Los objetos que tienen un tamaño dinámico deben ser gestionados por su propia clase (por ejemplo, 'std :: vector'): no los asigne _miles como miembros; haces que el contenedor sea el miembro. Y el lanzamiento a una clase base virtual o lo que sea implica que el miembro es polimórfico, que es lo que mencioné. –

+0

Muy bien, bastante bien –

2

Para obtener un poco de terminología: lo que usted llama heap y stack describe la vida útil de los objetos.La primera significa que la vida útil es dynamic, la segunda automatic y la tercera (que no menciona) es static.

Por lo general, necesitará dynamic vida útil de un objeto cuando debería durar más que el ámbito en el que se creó. Otro caso común es cuando desea que se comparta entre diferentes objetos principales. Además, la vida dinámica también es necesaria cuando se trabaja con un diseño que está fuertemente orientado a objetos (usa mucho polimorfismo, no usa valores), p. Qt.

Una expresión idiomática que requiere tiempos de vida dinámicos es el idioma pimpl.

La mayoría de las bibliotecas de programación genérica están más enfocadas hacia el valor y la semántica de valores, por lo que no utilizará mucho esa vinculación dinámica y las vidas automáticas se volverán mucho más comunes.

También hay algunos ejemplos donde se requiere la asignación dinámica por razones específicas más de ejecución:

  • objetos dinámicamente de tamaño (recipientes)
  • manejar tipos incompletas
  • (ver pimpl-idioma)
  • fácil nulabilidad de un tipo

Todas estas son solo pautas generales y deben decidirse caso por caso. En general, prefiere objetos automáticos sobre dinámicos.

0

Considere este ejemplo:

Implementa una lista vinculada que tiene un miembro de campo cabeza de nodo de clase.
Cada nodo tiene un miembro de campo next. Si este miembro del tipo Nodo y no Nodo * el tamaño de cada Nodo dependerá del número de nodos después de él en la cadena.

Por ejemplo, si tiene 100 nodos en su lista, su miembro principal será enorme. Porque contiene el siguiente nodo dentro de sí mismo, por lo que necesita tener el tamaño suficiente para mantenerlo y luego el siguiente y así sucesivamente. Por lo tanto, la cabeza debe tener espacio suficiente para contener 99 nodos los siguientes 98 y así sucesivamente ...

Desea evitar eso, así que en este caso es mejor tener un puntero al siguiente nodo en cada nodo en lugar de próximo nodo en sí.

Cuestiones relacionadas