Leí en algunos artículos que los punteros en bruto casi nunca deberían usarse. En su lugar, siempre deben estar envueltos dentro de punteros inteligentes, ya sean punteros de alcance o compartidos.
Sin embargo, noté que marcos como Qt, wxWidgets y bibliotecas como Boost nunca regresan ni esperan punteros inteligentes, como si no los estuvieran usando en absoluto. En cambio, regresan o esperan indicadores crudos. ¿Hay alguna razón para eso? ¿Debo mantenerme alejado de los punteros inteligentes cuando escribo una API pública y por qué?
Solo me pregunto por qué se recomiendan los punteros inteligentes cuando muchos proyectos importantes parecen evitarlos.
c++
api
pointers
smart-pointers
Laurent
fuente
fuente
unique_ptr
? Ninguno en absoluto. ¿Qt / WxWidgets están dirigidos a sistemas integrados o en tiempo real? No, están destinados a Windows / Mac / Unix en un escritorio, como máximo. Los punteros inteligentes son para programadores que quieren hacerlo bien.Respuestas:
Además del hecho de que muchas bibliotecas se escribieron antes del advenimiento de los punteros inteligentes estándar, la razón más importante es probablemente la falta de una interfaz binaria de aplicaciones (ABI) C ++ estándar.
Si está escribiendo una biblioteca de solo encabezado, puede pasar punteros inteligentes y contenedores estándar al contenido de su corazón. Su fuente está disponible para su biblioteca en el momento de la compilación, por lo que confía solo en la estabilidad de sus interfaces, no en sus implementaciones.
Pero debido a la falta de ABI estándar, generalmente no puede pasar estos objetos de forma segura a través de los límites del módulo. Un GCC
shared_ptr
es probablemente diferente de un MSVCshared_ptr
, que también puede diferir de un Intelshared_ptr
. Incluso con el mismo compilador, no se garantiza que estas clases sean binarias compatibles entre versiones.La conclusión es que si desea distribuir una versión preconstruida de su biblioteca, necesita un ABI estándar en el que confiar. C no tiene uno, pero los proveedores de compiladores son muy buenos con respecto a la interoperabilidad entre las bibliotecas de C para una plataforma determinada; existen estándares de facto.
La situación no es tan buena para C ++. Los compiladores individuales pueden manejar la interoperación entre sus propios binarios, por lo que tiene la opción de distribuir una versión para cada compilador compatible, a menudo GCC y MSVC. Pero a la luz de esto, la mayoría de las bibliotecas solo exportan una interfaz C, y eso significa punteros sin formato.
Sin embargo, el código que no es de biblioteca generalmente prefiere punteros inteligentes en lugar de sin formato.
fuente
Puede haber muchas razones. Para enumerar algunos de ellos:
Editar : El uso de punteros inteligentes es una elección completamente del desarrollador. Depende de varios factores.
En los sistemas críticos de rendimiento, es posible que no desee utilizar punteros inteligentes que generan gastos generales
El proyecto que necesita la compatibilidad con versiones anteriores, es posible que no desee utilizar los punteros inteligentes que tienen características específicas de C ++ 11
Edit2 Hay una cadena de varios votos negativos en el lapso de 24 horas debido al pasaje a continuación. No entiendo por qué se rechaza la respuesta, aunque a continuación se muestra solo una sugerencia adicional y no una respuesta.
Sin embargo, C ++ siempre te facilita tener las opciones abiertas. :) p.ej
Y en tu código úsalo como:
Para aquellos que dicen que un puntero inteligente y un puntero sin formato son diferentes, estoy de acuerdo con eso. El código anterior era solo una idea en la que uno puede escribir un código que sea intercambiable solo con un
#define
, esto no es una obligación ;Por ejemplo,
T*
tiene que eliminarse explícitamente, pero un puntero inteligente no. Podemos tener una plantillaDestroy()
para manejar eso.y úsalo como:
Del mismo modo, para un puntero sin formato podemos copiarlo directamente y para un puntero inteligente podemos usar una operación especial.
Donde
Assign()
es como:fuente
std::auto_ptr
que ha sido parte del estándar durante mucho tiempo (y tenga en cuenta que me gustastd::auto_ptr
como el tipo de retorno para funciones que crean objetos, incluso si es casi inútil en cualquier otro lugar). En C ++ 11std::unique_ptr
no tiene costos adicionales sobre un puntero simple.unique_ptr
y desaparición deauto_ptr
, el código dirigido a C ++ 03 debería usar el último, mientras que el código dirigido a C ++ 11 puede usar el primero. Los punteros inteligentes no lo sonshared_ptr
, hay muchos estándares y ninguno estándar, incluidas las propuestas al estándar que fueron rechazadas comomanaged_ptr
unique_ptr
incurren en costos de tiempo de ejecución, perounique_ptr
es, con mucho, el que se usa más comúnmente. El ejemplo de código que usted proporciona también es engañoso, puesunique_ptr
, yT*
son completamente diferentes conceptos. El hecho de que se refiera a ambos comotype
da la impresión de que pueden intercambiarse entre sí.Hay dos problemas con los punteros inteligentes (pre C ++ 11):
El puntero inteligente predeterminado , en el sentido de que es gratuito, es
unique_ptr
. Desafortunadamente, requiere semántica de movimiento C ++ 11, que solo apareció recientemente. Todos los demás punteros inteligentes tienen un costo (shared_ptr
,intrusive_ptr
) o tienen una semántica menos que ideal (auto_ptr
).Con C ++ 11 a la vuelta de la esquina, trayendo un
std::unique_ptr
, uno estaría tentado a pensar que finalmente se terminó ... No soy tan optimista.Solo unos pocos compiladores importantes implementan la mayoría de C ++ 11, y solo en sus versiones recientes. Podemos esperar que las principales bibliotecas, como QT y Boost, estén dispuestas a mantener la compatibilidad con C ++ 03 por un tiempo, lo que impide de alguna manera la adopción generalizada de los nuevos y brillantes punteros inteligentes.
fuente
No debe mantenerse alejado de los punteros inteligentes, tienen su uso especialmente en aplicaciones donde tiene que pasar un objeto.
Las bibliotecas tienden a devolver un valor o completar un objeto. Por lo general, no tienen objetos que deban usarse en muchos lugares, por lo que no es necesario que usen punteros inteligentes (al menos no en su interfaz, pueden usarlos internamente).
Podría tomar como ejemplo una biblioteca en la que hemos estado trabajando, donde después de unos meses de desarrollo me di cuenta de que solo usamos punteros y punteros inteligentes en algunas clases (3-5% de todas las clases).
Pasar variables por referencia fue suficiente en la mayoría de los lugares, usamos punteros inteligentes cada vez que teníamos un objeto que podía ser nulo, y punteros sin procesar cuando una biblioteca que usábamos nos obligaba a hacerlo.
Editar (no puedo comentar debido a mi reputación): pasar variables por referencia es muy flexible: si desea que el objeto sea de solo lectura, puede usar una referencia constante (aún puede hacer algunos cambios desagradables para poder escribir el objeto) ) pero obtienes la máxima protección posible (es lo mismo con los punteros inteligentes). Pero sí estoy de acuerdo en que es mucho mejor devolver el objeto.
fuente
Qt reinventó sin sentido muchas partes de la biblioteca estándar en un intento de convertirse en Java. Creo que ahora tiene sus propios punteros inteligentes, pero en general, no es un pináculo de diseño. wxWidgets, hasta donde yo sé, fue diseñado mucho antes de que se escribieran los punteros inteligentes utilizables.
En cuanto a Boost, espero que utilicen punteros inteligentes siempre que sea apropiado. Puede que tenga que ser más específico.
Además, no olvide que existen punteros inteligentes para imponer la propiedad. Si la API no tiene semántica de propiedad, ¿por qué usar un puntero inteligente?
fuente
QString
, wxWidgets tienewxString
, MFC tiene el nombre horribleCString
. ¿No es un UTF-8std::string
lo suficientemente bueno para el 99% de las tareas de GUI?Buena pregunta. No conozco los artículos específicos a los que se refiere, pero he leído cosas similares de vez en cuando. Mi sospecha es que los escritores de tales artículos tienden a albergar un sesgo en contra de la programación estilo C ++. Si el escritor programa en C ++ solo cuando debe hacerlo, luego regresa a Java o similar tan pronto como pueda, entonces realmente no comparte la mentalidad de C ++.
Uno sospecha que algunos o la mayoría de los mismos escritores prefieren los administradores de memoria recolectores de basura. No lo hago, pero pienso de manera diferente que ellos.
Los punteros inteligentes son geniales, pero tienen que mantener el recuento de referencias. El mantenimiento de los recuentos de referencia conlleva costos, a menudo costos modestos, pero no obstante, en el tiempo de ejecución. No hay nada de malo en ahorrar estos costos mediante el uso de punteros vacíos, especialmente si los punteros son administrados por destructores.
Una de las cosas excelentes de C ++ es su compatibilidad con la programación de sistemas integrados. El uso de punteros desnudos es parte de eso.
Actualización: Un comentarista ha observado correctamente que el nuevo C ++
unique_ptr
(disponible desde TR1) no cuenta referencias. El comentarista también tiene una definición diferente de "puntero inteligente" que tengo en mente. Puede tener razón sobre la definición.Actualización adicional: el hilo de comentarios a continuación es esclarecedor. Todo es lectura recomendada.
fuente
shared_ptr
mantiene un recuento de referencia. Hay muchos otros tipos de punteros inteligentes que no mantienen ningún recuento de referencia. Finalmente, las bibliotecas mencionadas están destinadas a plataformas que tienen muchos recursos de sobra. No es que fuera el votante, pero todo lo que digo es que tu publicación está llena de errores.shared_ptr
No tiene gastos generales. Solo tiene gastos generales si no necesita una semántica de propiedad compartida segura para subprocesos, que es lo que proporciona.También hay otros tipos de punteros inteligentes. Es posible que desee un puntero inteligente especializado para algo como la replicación de la red (uno que detecta si se accede y envía modificaciones al servidor o algo así), mantiene un historial de cambios, marca el hecho de que se accedió para que se pueda investigar cuando guarda datos en el disco, etc. No estoy seguro de si hacer eso en el puntero es la mejor solución, pero el uso de los tipos de puntero inteligente integrados en las bibliotecas podría provocar que las personas se enganchen en ellos y pierdan la flexibilidad.
Las personas pueden tener todo tipo de requisitos y soluciones de administración de memoria diferentes más allá de los punteros inteligentes. Es posible que desee administrar la memoria yo mismo, podría estar asignando espacio para cosas en un grupo de memoria para que se asigne por adelantado y no en tiempo de ejecución (útil para juegos). Podría estar usando una implementación de C ++ recolectada de basura (C ++ 11 lo hace posible aunque todavía no existe). O tal vez simplemente no estoy haciendo nada lo suficientemente avanzado como para preocuparme por molestarme con ellos, puedo saber que no voy a olvidar los objetos no inicializados, etc. Tal vez solo confío en mi capacidad para administrar la memoria sin la muleta del puntero.
La integración con C es otro problema también.
Otro problema es que los punteros inteligentes son parte de la STL. C ++ está diseñado para ser utilizable sin el STL.
fuente
También depende del dominio en el que trabajes. Escribo motores de juego para vivir, evitamos el impulso como la peste, en los juegos la sobrecarga del impulso no es aceptable. En nuestro motor central terminamos escribiendo nuestra propia versión de stl (al igual que ea stl).
Si tuviera que escribir una aplicación de formularios, podría considerar el uso de punteros inteligentes; pero una vez que la gestión de la memoria es una segunda naturaleza, no tener un control granular sobre la memoria se vuelve silencioso y molesto.
fuente