Leí en alguna parte que cuando se usa C ++ se recomienda no usar punteros. ¿Por qué los punteros son una mala idea cuando estás usando C ++? Para los programadores de C que están acostumbrados a usar punteros, ¿cuál es la mejor alternativa y enfoque en C ++?
45
NetConnection
instancias que se desconectan del servidor ( stackoverflow.com/questions/14780456/… ), así como un problema con la existencia de múltiples objetos en un programa que específicamente se negará a recopilar. ...GCRoots are never garbage collected.
y el párrafo comenzó porThe MMgc is considered a conservative collector for mark/sweep.
). Técnicamente, este es un problema en Adobe Virtual Machine 2, no en el AS3 en sí, pero cuando tiene problemas como este en lenguajes de nivel superior, que tienen esencialmente la recolección de basura incorporada, a menudo no tiene ninguna forma verdadera en el lenguaje para depurar estos problemas completamente fuera del programa. ...Respuestas:
Creo que significan que debes usar punteros inteligentes en lugar de punteros regulares.
En C ++, el énfasis estaría en la recolección de basura y la prevención de pérdidas de memoria (solo por nombrar dos). Los punteros son una parte fundamental del lenguaje, por lo que no usarlos es prácticamente imposible, excepto en los programas más triviales.
fuente
Como yo fui quien publicó la polémica "no use punteros" , siento que debería comentar aquí.
En primer lugar, como polémica, obviamente representa un punto de vista extremo. No son definitivamente los usos legítimos de punteros (crudos). Pero yo (y muchos programadores profesionales de C ++) sostienen que estos casos son extremadamente raros. Pero lo que realmente queremos decir es lo siguiente:
Primero:
Aquí, "memoria propia" esencialmente significa que en algún momento
delete
se llama a ese puntero (pero es más general que eso). Esta declaración se puede tomar con seguridad como un absoluto. La única excepción es cuando implementa su propio puntero inteligente (u otra estrategia de administración de memoria). Y hasta allí se debe normalmente todavía utilizar un puntero inteligente en el nivel bajo.La razón para esto es bastante simple: los punteros sin procesar que poseen memoria introducen una fuente de error. Y estos errores son prolíficos en el software existente: fugas de memoria y doble eliminación, ambas una consecuencia directa de la propiedad de recursos poco clara (pero en dirección opuesta).
Este problema se puede eliminar por completo, prácticamente sin costo, simplemente usando punteros inteligentes en lugar de punteros sin procesar (advertencia: esto todavía requiere pensar, por supuesto; los punteros compartidos pueden conducir a ciclos y, por lo tanto, una vez más a pérdidas de memoria, pero esto es fácil evitable).
Segundo:
A diferencia de otros lenguajes, C ++ tiene un soporte muy fuerte para la semántica de valor y simplemente no necesita la indirecta de los punteros. Esto no se realizó de inmediato: históricamente, C ++ se inventó para facilitar la orientación fácil de objetos en C, y se basó en gran medida en la construcción de gráficos de objetos que estaban conectados por punteros. Pero en C ++ moderno, este paradigma rara vez es la mejor opción, y los modismos modernos de C ++ a menudo no necesitan punteros . Operan en valores en lugar de punteros.
Desafortunadamente, este mensaje aún no se ha aplicado en gran parte de la comunidad de usuarios de C ++. Como resultado, la mayor parte del código de C ++ que está escrito todavía está lleno de punteros superfluos que hacen que el código sea complejo, lento y defectuoso / poco confiable.
Para alguien que conoce moderna C ++, está claro que muy raramente necesita ningún punteros inteligentes (ya sea o primas, excepto cuando se utiliza como iteradores). El código resultante es más corto, menos complejo, más legible, a menudo más eficiente y más confiable.
fuente
std::unique_ptr
. Además, ¿por qué noptr_vec
? Pero por lo general, un vector de valores con todavía se intercambiará más rápido (especialmente con semántica de movimiento).boost::variant
con arecursive_wrapper
es probablemente mi solución favorita para representar un DAG.Simplemente porque hay abstracciones disponibles que ocultan los aspectos más temperamentales del uso de punteros, como el acceso a la memoria sin procesar y la limpieza después de las asignaciones. Con punteros inteligentes, clases de contenedores y patrones de diseño como RAII, la necesidad de usar punteros sin procesar se reduce. Dicho esto, como cualquier abstracción, debes entender cómo funcionan realmente antes de ir más allá de ellos.
fuente
Relativamente simple, la mentalidad C es "¿Tienes un problema? Usa un puntero". Puede ver esto en cadenas C, punteros de función, punteros como iteradores, puntero a puntero, puntero vacío, incluso en los primeros días de C ++ con punteros miembros.
Pero en C ++ puede usar valores para muchas o todas estas tareas. ¿Necesitas una función de abstracción?
std::function
. Es un valor que es una función.std::string
? Es un valor, es una cadena. Puede ver enfoques similares en todo C ++. Esto hace que analizar el código sea mucho más fácil tanto para humanos como para compiladores.fuente
Una de las razones es la aplicación demasiado amplia de punteros. Se pueden usar para la iteración sobre contenedores, para evitar copiar objetos grandes al pasar a la función, administración de tiempo de vida no trivial, acceder a lugares aleatorios en la memoria, etc. Y una vez que los usó para un propósito, otras características están disponibles. inmediatamente independientemente de la intención.
La selección de una herramienta para el propósito exacto hace que el código sea más simple y la intención más visible: iteradores para iteraciones, punteros inteligentes para la gestión de la vida útil, etc.
fuente
Además de las razones ya mencionadas, hay una obvia: mejores optimizaciones. El análisis de alias es demasiado complicado en presencia de una aritmética de puntero, mientras que las referencias sugieren un optimizador, por lo que es posible un análisis de alias mucho más profundo si solo se usan referencias.
fuente
Además del riesgo de pérdidas de memoria indicadas por @jmquigley, el puntero y la aritmética del puntero pueden considerarse problemáticos porque los punteros pueden apuntar a todas partes en la memoria causando "errores difíciles de encontrar" y "vulnerabilidades de seguridad".
Es por eso que casi fueron abandonados en C # y Java.
fuente
unsafe
palabra claveC ++ es compatible con la mayoría de C , características, además de objetos y clases. C ya tenía punteros y otras cosas.
Los punteros son una técnica muy útil, que se puede combinar con la orientación a objetos, y C ++ los admite. Pero esta técnica es difícil de enseñar y de entender, y es muy fácil causar errores no deseados.
Muchos lenguajes de programación nuevos pretenden no utilizar punteros con objetos, como Java, .NET, Delphi, Vala, PHP, Scala. Pero, los punteros todavía se usan, "detrás de escena". Estas técnicas de "puntero oculto" se denominan "referencias".
De todos modos, considero puntero (s) como un patrón de programación, como una forma válida de resolver ciertos problemas, así como lo hace la programación orientada a objetos .
Otros desarrolladores pueden tener una opinión diferente. Pero, sugiero que los estudiantes y programadores aprendan cómo:
(1) Use punteros sin objetos
(2) objetos sin punteros
(3) punteros explícitos a objetos
(4) punteros "ocultos" a los objetos ( referencia AKA ) ;-)
En ese orden.
Incluso si es difícil de enseñar y difícil de aprender. Object Pascal (Delphi, FreePascal, otros) y
C++
(no Java o C #) se pueden usar para esos objetivos.Y, más tarde, los programadores novatos pueden pasar a lenguajes de programación de "punteros ocultos a objetos" como: Java, C #, PHP orientado a objetos y otros.
fuente
Hablando de VC6, cuando convierte un puntero de una clase (que crea una instancia) en una variable (por ejemplo, DWORD), incluso si este puntero es local, puede acceder a la clase sobre todas las funciones que usan el mismo montón. La clase instanciada se define como local pero, de hecho, no lo es. Hasta donde sé, cualquier dirección de una variable, estructura o clase de montón es única a lo largo de toda la vida de la clase de alojamiento.
Ejemplo:
EDITAR Esa es una muy pequeña parte del código original. La clase CSRecodset es solo una clase de conversión de CXdbRecordset, donde está todo el código real. Al hacerlo, puedo permitir que el usuario se beneficie de lo que escribí sin perder mis derechos. No pretendo demostrar que mi motor de base de datos es profesional, pero realmente funciona.
EDITAR: solicitado por DeadMG:
fuente
DWORD
es abusivo y posiblemente incorrecto (DWORD no es necesariamente lo suficientemente ancho como para sostener un puntero). Si necesita un puntero sin tipo, úselo,void*
pero cuando lo necesite en C ++, a menudo tiene un problema de diseño en su código que debe solucionar.