2009-03-18 30 views
16

Después de leer algunos tutoriales, llegué a la conclusión de que uno siempre debe usar punteros para los objetos. Pero también he visto algunas excepciones al leer algunos tutoriales de QT (http://zetcode.com/gui/qt4/painting/) donde se crea el objeto QPaint en la pila. Entonces ahora estoy confundido. ¿Cuándo debería usar punteros?C++: cuándo usar punteros?

+0

Los tutoriales de QT crean el objeto QPaint en la pila porque la vida útil del objeto QPaint se conoce en tiempo de compilación y está delimitada exactamente por un par coincidente de {}. –

+0

Lo mismo es válido para QPen, QPainter y QApplication. –

+0

Lo que es bueno saber sobre puntero es que ahora tenemos en C++ alguna implementación segura de punteros como unique_ptr o weak_ptr, cuando necesite usar un puntero, debe usarlos (con alguna extraña excepción en el código de bajo nivel) – MokaT

Respuesta

41

Si usted no sabe cuándo se debe utilizar punteros simplemente no utilizarlas.

Aparecerá cuando las necesite, cada situación es diferente. No es fácil resumir concisamente cuando deberían usarse. No adquiera el hábito de "usar siempre punteros para objetos", sin duda es un mal consejo.

+6

Esto no es una respuesta. El objetivo de este sitio es ayudar a las personas a aprender más, no a desestimarlas cuando no puede articular sus pensamientos. –

+0

@ A.R. Afortunadamente, dejé que Jamolkhon aprendiera a no usar siempre los punteros, mire la primera oración de su pregunta. Tomé ese contexto en cuenta, no solo el título. Además, otras preguntas enumeran algunos de los usos. – CiscoIPPhone

+0

Tomé la última oración en cuenta, ya sabes, la parte de la pregunta. –

4

¿Qué tutoriales serían? En realidad, la regla es que debe usar punteros solo cuando es absolutamente necesario, lo cual es bastante raro. Necesita leer un buen libro en C++, como Accelerated C++ por Koenig & Moo.

Editar: Para aclarar un poco - dos casos en los que le no utilizar un puntero (cadena se utiliza aquí como un ejemplo - mismo podría ir para cualquier otro tipo):

class Person { 
    public: 
     string name;  // NOT string * name; 
    ... 
}; 


void f() { 
    string value;  // NOT string * value 
    // use vvalue 
} 
+0

¿Qué tal? las limitaciones de la pila? – Jamol

+0

¿qué limitación sería eso? –

+0

bueno, quiero decir en algunos sistemas, si no todo el tamaño de la pila es limitado, ¿no? – Jamol

21

Motivos principales para usar punteros:

  1. duración del objeto de control;
  2. no se pueden usar referencias (por ejemplo, si desea almacenar algo que no se puede copiar en el vector);
  3. debe pasar el puntero a alguna función de terceros;
  4. tal vez algunas razones de optimización, pero no estoy seguro.
+4

Creo que el punto más importante de los punteros es controlar la vida útil de los objetos –

+0

El punto n. ° 2 se aplica particularmente a Qt ya que la mayoría de los objetos relacionados con los gráficos Qt no se pueden copiar. –

+0

@caparcode, En realidad, QObjects no se pueden copiar. Eso incluye elementos de GUI y muy pocas clases de gráficos, y algunas otras cosas. Otras clases pueden no ser copiables también, por la razón que sea. – strager

1

suelen utilizar punteros/referencias a objetos cuando:

  • pasarlos a otros métodos

  • creando una gran variedad (no estoy seguro de lo que el tamaño normal de la pila es)

Utilice la pila cuando:

  • Estás creando un objeto que vive y muere dentro del método

  • El objeto es del tamaño de un registro de la CPU o menores

+0

Casi todo lo que dices aquí es incorrecto. –

+0

Sí, estás muy equivocado. –

+0

Entonces, ¿ustedes dos: pasan objetos en la pila, crean grandes matrices en la pila, asignan variables locales en el montón, asignan enteros en el montón? – LegendLength

0

utilizar punteros cuando no se desea que el objeto de ser destruido cuando el marco de pila se vacía.

Utilice las referencias para pasar parámetros cuando sea posible.

+0

int x; int * pointer_to_x = & x; Está repitiendo la confusión del OP entre los punteros y la asignación del montón. –

+0

No, pasar por referencia constante en lugar de valor es un buen hábito para entrar. Si bien utiliza punteros en el nivel de implementación, no tiene nada que ver con ellos conceptualmente. –

4

Por lo general, tiene que utilizar punteros en los siguientes escenarios:

  1. Se necesita una colección de objetos que pertenecen a diferentes clases (en la mayoría de los casos tendrán una base común).

  2. Necesita una colección de objetos asignada por la pila tan grande que probablemente cause el desbordamiento de la pila.

  3. Necesita una estructura de datos que pueda reorganizar los objetos rápidamente, como una lista vinculada, un árbol similar.

  4. Necesita una lógica compleja de administración de por vida para su objeto.

  5. Necesita una estructura de datos que permita la navegación directa de un objeto a otro, como una lista vinculada, un árbol o cualquier otro gráfico.

+0

En el caso de (1) no existe "tal vez" la necesidad de una base común. En el caso de (2) la única "colección" donde se aplicaría esto sería una matriz tipo C. –

+0

Para el caso (1) no es necesario, es posible que desee almacenar un conjunto de void * para poder llamar a free(): esa no es la manera C++ de hacer las cosas, pero podría ser. Para el caso (2) me refiero a una matriz de punteros frente a una matriz de objetos reales. Si los objetos son realmente grandes, los punteros de almacenamiento ahorran mucho espacio en la pila. – sharptooth

2

Además de los puntos que otros hacen (especialmente w.r.t. controlar la duración del objeto), si necesita manejar objetos NULL, debe usar punteros, no referencias. Es posible crear una referencia NULL mediante encasillamiento, pero generalmente es una mala idea.

+0

En realidad es una idea bastante horrible. –

1

De hecho, me utilizar punteros en esta situación:

class Foo 
{ 
    Bar* bar; 

    Foo(Bar& bar) : bar(&bar) { } 

    Bar& Bar() const { return *bar; } 
}; 

Antes de eso, los miembros de referencia utilizado, que se inicia desde el constructor, pero el compilador tiene un problema al crear constructores de copia, operadores de asignación, y el lote.

de Dave

15

No me queda claro si su pregunta es-PTR-a obj vs o PTR-a-obj obj vs referencia a basada en obj-pila. También hay usos que no entran en ninguna categoría.

En cuanto a vs stack, eso parece estar cubierto anteriormente. Varias razones, la más obvia es la vida del objeto.

En cuanto vs referencias, siempre tratamos de utilizar referencias, pero hay cosas que se pueden ver sólo con las PAD, por ejemplo (hay muchos usos):

  • caminando a través de elementos de una matriz (por ejemplo, marchando a través de una matriz estándar [])
  • cuando una función llamada asigna algo & lo devuelve a través de un PTR

que es más importante, los punteros (y referencias, a diferencia de automática/apilar-basada & objetos estáticos) apoyan el polimorfismo. Un puntero a una clase base en realidad puede apuntar a una clase derivada. Esto es fundamental para el comportamiento OO soportado en C++.

+3

+1 para el último, en negrita, párrafo, porque no puedo expresarlo mejor yo mismo. –

+0

Pero puede asignar algo en la pila y luego utilizarlo más tarde polimórficamente pasando una referencia o un puntero a su función/contenedor/lo que sea. ** La duración del almacenamiento y el polimorfismo son conceptos ortogonales. ** Sin embargo, las personas continúan respondiendo preguntas sobre por qué uno podría asignar el puntero citando el polimorfismo como una ventaja, sin explicar nunca por qué los dos están relacionados (no lo son). –

10

En primer lugar, la pregunta es incorrecta: el dilema no es entre los punteros y la pila, sino entre el montón y la pila. Puede tener un objeto en la pila y pasar el puntero a ese objeto. Supongo que lo que realmente está preguntando es si debería declarar un puntero a clase o una instancia de clase.

La respuesta es que depende de lo que quiera hacer con el objeto. Si el objeto debe existir después de que el control abandone la función, entonces tiene que usar un puntero y crear el objeto en el montón. Hará esto, por ejemplo, cuando su función tenga que devolver el puntero al objeto creado o agregar el objeto a una lista que se creó antes de llamar a su función.

Por otro lado, si los objetos son locales para la función, entonces es mejor usarlos en la pila. Esto permite que el compilador llame al destructor cuando el control abandona la función.

1

utilizando punteros se conecta con dos cosas ortogonales:

  1. asignación dinámica. En general, debe asignar dinámicamente, cuando el objeto está destinado a vivir más tiempo que el ámbito en el que se creó. Tal objeto es un recurso cuyo propietario debe estar claramente especificado (más comúnmente, algún tipo de puntero inteligente).

  2. Accediendo por la dirección (independientemente de cómo se haya creado el objeto). En este contexto, puntero no significa propiedad. Tal acceso podría ser necesario cuando:

    • alguna interfaz ya existente lo requiere.
    • asociación que podría ser nula debe modelarse.
    • se debe evitar la copia de objetos grandes o la copia no es posible en absoluto, pero la referencia no se puede usar (por ejemplo, colecciones stl).

El # 1 y # 2 puede producirse en diferentes configuraciones, por ejemplo se puede imaginar objeto asignado dinámicamente visitada por puntero, pero tal objeto también por podrían pasar por referencia a alguna función. También puede obtener un puntero a algún objeto que se crea en la pila, etc.

+0

"dos cosas ortogonales" - finalmente, ¡alguien lo dijo! [cf] (http://stackoverflow.com/questions/658133/c-when-to-use-pointers#comment63979527_658308) Añadiría al punto ** 2 ** que el acceso por referencia/puntero (= dirección) permite el polimorfismo , independientemente de cómo se haya asignado el objeto, contrariamente a la desconcertante cantidad de publicaciones que veo implican que una razón válida para la asignación dinámica es otorgar un comportamiento polimórfico, que no es necesario, no es un argumento, y no está relacionado por completo. –

1

Pase por valor con objetos copiables de buen comportamiento es la forma de obtener una gran cantidad de su código.

Si la velocidad realmente importa, utilice pasar por referencia donde pueda, y finalmente utilice punteros.

0

Hablando de C++, los objetos creados en la pila no se pueden usar cuando el programa ha abandonado el ámbito en el que se creó. Por lo general, cuando sabe que no necesita una variable para pasar una función, puedes crearlo en la pila.

Hablando específicamente de Qt, Qt ayuda al programador al manejar gran parte de la administración de memoria de los objetos de montón. Para los objetos que se derivan de QObject (casi todas las clases con el prefijo "Q" son), los constructores toman un parámetro opcional principal. El padre entonces posee el objeto, y cuando el padre se elimina, todos los objetos propios también se eliminan. En esencia, la responsabilidad de la destrucción de los niños se pasa al objeto principal. Al utilizar este mecanismo, se debe crear el elemento secundario QObject s en el montón.

En resumen, en Qt puedes crear fácilmente objetos en el montón, y siempre que establezcas un elemento primario adecuado, solo tendrás que preocuparte por destruir el elemento primario. En general, C++, sin embargo, tendrá que recordar destruir objetos de montón, o usar punteros inteligentes.

1

De ser posible, nunca use punteros. Confíe en pasar por referencia o si va a devolver una estructura o clase, suponga que su compilador tiene una optimización del valor de retorno. (Sin embargo, debe evitar la construcción condicional de la clase devuelta).

Hay una razón por la que Java no tiene punteros. C++ tampoco los necesita.Si evita su uso, obtendrá el beneficio adicional de la destrucción automática de objetos cuando el objeto abandone el alcance. De lo contrario, su código generará errores de memoria de varios tipos. Las fugas de memoria pueden ser muy difíciles de encontrar y a menudo ocurren en C++ debido a excepciones no controladas.

Si debe usar punteros, considere algunas de las clases de punteros inteligentes como auto_ptr. La destrucción automática de objetos es más que simplemente liberar la memoria subyacente. Hay un concepto llamado RAII. Algunos objetos requieren, además, la destrucción. p.ej. mutexes y archivos de cierre, etc.

+1

Bueno, Java no tiene punteros porque todo lo que no sea una primitiva es implícitamente un puntero. – mk12