2010-11-02 18 views
14

Estoy programando en C++ en este momento, y me encanta usar punteros. Pero parece que otros lenguajes más nuevos como Java, C# y Python no le permiten declarar explícitamente punteros. En otras palabras, no puede escribir int x y int * y, y tener x como valor, mientras que y es un puntero, en cualquiera de esos idiomas. ¿Cuál es el razonamiento detrás de esto?¿Los indicadores son malos?

+1

la mayoría de la gente diría que pueden ser peligrosos, debido a la capacidad de desordenar y crear fallas seg y o pérdidas de memoria. – Jim

+6

¿Qué estás haciendo con los indicadores que te encantan? – GManNickG

+0

@ GMan - Creo que mi cosa favorita es permitir que una clase 'A' contenga punteros a otra clase' B', de modo que si un objeto de 'B' es en realidad una clase' C' heredada de 'B', entonces 'A' puede llamar a las funciones' C' en lugar de a las funciones virtuales 'B'. Supongo que otros lenguajes podrían implementarse de manera diferente para evitar esa necesidad de punteros. Además, puede hacer que dos clases incluyan punteros entre sí para que ambas puedan acceder a las funciones de cada una, pero no puede hacer eso con valores porque crearía un ciclo infinito con la clase 'A' que contiene la clase' B' que contiene la clase 'A '... – wrongusername

Respuesta

17

Los apuntadores no son malos, solo es fácil equivocarse. En los idiomas más nuevos, han encontrado formas de hacer las mismas cosas, pero con menos riesgo de dispararse en el pie.

Sin embargo, no hay nada de malo en los punteros. Adelante, ámalos.

Para su ejemplo, ¿por qué querría que tanto x como y apuntaran a la misma memoria? ¿Por qué no llamarlo siempre x?

Un punto más, los punteros significan que usted tiene que administrar la vida de la memoria usted mismo. Los idiomas más nuevos prefieren usar la recolección de basura para administrar la memoria y permitir punteros haría que esa tarea fuera bastante difícil.

+0

Bueno, no quiero decir que 'x' y' y' apunten a la misma dirección de memoria para siempre, solo que no se puede declarar 'x' como almacenamiento de un valor y' y' como almacenamiento de una dirección de memoria. No obstante, no sé si prefiero poder elegir explícitamente siempre entre declarar una variable con un valor o una referencia. – wrongusername

+12

Por supuesto, nada bueno es gratis. Los indicadores de C++ son potentes y peligrosos, y en algunos de los otros idiomas, si bien no puede dispararse en el pie, le resulta difícil apuntar hacia abajo. Hay cosas que aprender de cómo los lenguajes de nivel superior manejan punteros que se pueden aplicar a C++. – ssube

+0

@peachykeen: +1 por la metáfora inteligente. – Cam

6

Se puede abusar de los punteros, y los idiomas administrados prefieren protegerlo de peligros potenciales. Sin embargo, los punteros ciertamente no son malos, son una característica integral de los lenguajes C y C++, y escribir código C/C++ sin ellos es complicado y engorroso.

12

I'll start with one of my favorite Scott Meyers quotes:

Cuando doy charlas sobre el manejo de excepciones, enseño a la gente dos cosas:

  • punteros son vuestros enemigos, porque conducen a los tipos de problemas que auto_ptr está diseñado para eliminar.

  • LOS PUNTEROS SON SUS AMIGOS, porque las operaciones con punteros no pueden arrojarse.

Entonces les digo que tenga un buen día :-)


El punto es que los punteros son extremadamente útiles y ciertamente es necesario entenderlos cuando se programa en C++. No puede entender el modelo de memoria C++ sin entender los punteros. Cuando está implementando una clase propietaria de recursos (como un puntero inteligente, por ejemplo), necesita utilizar punteros, y puede aprovechar su garantía de no-tiro para escribir clases propietarias de recursos seguros para excepciones.

Sin embargo, en un código de aplicación C++ bien escrito, nunca debería tener que trabajar con punteros sin formato. Nunca. Siempre se debe utilizar alguna capa de abstracción en lugar de trabajar directamente con los punteros:

  • Utilizar referencias en lugar de punteros siempre que sea posible. Las referencias no pueden ser nulas y hacen que el código sea más fácil de entender, más fácil de escribir y más fácil de revisar.

  • Utilice punteros inteligentes para administrar los punteros que utilice. Los indicadores inteligentes como shared_ptr, auto_ptr y unique_ptr ayudan a garantizar que no se pierdan recursos ni se liberen recursos prematuramente.

  • Utilice contenedores como los que se encuentran en la biblioteca estándar para almacenar colecciones de objetos en lugar de asignar matrices usted mismo. Al usar contenedores como vector y map, puede asegurarse de que su código sea excepcionalmente seguro (lo que significa que incluso cuando se lanza una excepción, no perderá recursos).

  • Use iteradores cuando trabaje con contenedores. Es mucho más fácil utilizar iteradores correctamente que usar punteros correctamente, y muchas implementaciones de biblioteca proporcionan soporte de depuración para ayudarlo a encontrar dónde los está utilizando incorrectamente.

  • Cuando trabaje con API heredadas o de terceros y debe usar punteros sin procesar, escriba una clase para encapsular el uso de esa API.

C++ tiene gestión automática de recursos en forma de Gestión de Recursos Ámbito-Bound (SBRM, también llamado Raii, o RAII). Úselo. Si no lo estás usando, lo estás haciendo mal.

+1

Las referencias pueden ser 'nulas', por ejemplo, cuando hace referencia a un puntero inválido sin referencia. Consulte: http://codepad.org/JEPQpv2v – Paul

+8

@Paul: el comportamiento de su fragmento de código no está definido. "Una referencia nula no puede existir en un programa bien definido, porque la única forma de crear dicha referencia sería vincularla al" objeto "obtenido desreferenciando un puntero nulo, , lo que causa un comportamiento indefinido" (C++ 03 8.3.2/4). –

+1

@James McNellis: El acceso a un puntero nulo también es un comportamiento indefinido, pero eso no viene al caso. El punto era, es que es completamente posible tener una referencia 'no válida' (es decir, 'nula'/agujero negro de comportamiento indefinido/etc). Puede estar llegando un poco, pero definitivamente es divertido depurar cuando se encuentra ... – Paul

3

Un verdadero "puntero" tiene dos características.

  • Se mantiene la dirección de otro objeto (o primitivo)
    • y expone la naturaleza de esa dirección numérica para que pueda realizar operaciones aritméticas.

Típicamente las operaciones aritméticas definidas para los punteros son:

  1. Adición de un número entero a un puntero en una matriz, que devuelve la dirección del otro elemento.
  2. Restando dos punteros en la misma matriz, que devuelve el número de elementos intermedios (incluido un extremo).
  3. Comparando dos punteros en la misma matriz, que indica qué elemento está más cerca de la cabeza de la matriz.

Los lenguajes administrados generalmente lo llevan por el camino de las "referencias" en lugar de los punteros. Una referencia también contiene la dirección de otro objeto (o primitiva), pero no se permite la aritmética.

Entre otras cosas, esto significa que no puede usar la aritmética del puntero para salir del final de una matriz y tratar algunos otros datos con el tipo incorrecto. La otra forma de formar un puntero no válido se soluciona en dichos entornos mediante el uso de recolección de elementos no utilizados.

Juntos esto asegura la seguridad del tipo, pero a una terrible pérdida de generalidad.

+0

No veo cómo se pierde la generalidad. El hecho de no tener punteros solo te obliga a llamar a una espada una pala: si quieres una matriz de elementos, la declaras como una matriz de elementos y utilizas índices enteros, que * pueden * manipularse numéricamente. (Tenga en cuenta que en C y C++, la aritmética del puntero se permite formalmente SÓLO entre punteros que apuntan al mismo bloque de memoria, es decir, matriz). – zvrba

+0

@zvrba: El hecho de que los lenguajes administrados no le permitan formar una referencia a un subobjeto es una ENORME pérdida de generalidad.No poder escribir un juego de palabras con char * es una ENORME pérdida de generalidad. Además, mi respuesta enfatiza que ambos indicadores deben estar en el mismo bloque. Sin embargo, los punteros a diferentes objetos tienen un orden indefinido pero ** estable **, en entornos administrados esta garantía ya no se hace, al recolector de basura se le permite no solo mover objetos sino cambiar su orden. –

+0

¿Qué quiere decir con "pérdida de generalidad"? Y los moldes inseguros (por ejemplo, char *) no son una propiedad inherente de los punteros, es solo una característica C. – zvrba

3

trato de responder a la pregunta de OP directamente:

En otras palabras, no se puede escribir tanto int x e int * y, y tienen x un valor mientras que y es un puntero, en cualquiera de esos idiomas. ¿Cuál es el razonamiento detrás de esto?

La razón detrás de esto es el modelo de memoria administrada en estos idiomas.En C# (o Python, o Java, ...) la vida útil de los recursos y, por lo tanto, el uso de memoria se gestiona automáticamente por el tiempo de ejecución subyacente o por recolector de basura, para ser precisos. En pocas palabras: la aplicación no tiene control sobre la ubicación de un recurso en la memoria. No se especifica, e incluso no se garantiza que se mantenga constante durante la vida útil de un recurso. Por lo tanto, la noción de puntero como 'una ubicación de algo en la memoria virtual o física' es completamente irrelevante.

0

Como alguien ya ha mencionado, los punteros pueden, y de hecho, saldrán mal si tiene una aplicación masiva. Esta es una de las razones por las que a veces vemos que Windows tiene problemas debido a los punteros NULL creados. Personalmente, no me gustan los indicadores porque causan pérdidas terribles de memoria y no importa qué tan bueno manejes tu memoria, eventualmente te perseguirá de alguna manera. Experimenté esto mucho con OpenCV al trabajar con aplicaciones de procesamiento de imágenes. Tener muchos apuntadores flotando alrededor, ponerlos en una lista y luego recuperarlos más tarde me causó problemas. Pero, una vez más, hay buenos lados en el uso de punteros y, a menudo, es una buena forma de ajustar tu código. Todo depende de lo que esté haciendo, las especificaciones que tenga que cumplir, etc.

Cuestiones relacionadas