He estado programando en C ++ por un tiempo, pero la mayoría de las cosas se centraron en las características de bajo nivel de C ++. Con eso me refiero principalmente a trabajar con punteros y matrices sin procesar. Creo que este comportamiento se conoce como usar C ++ como C con clases. A pesar de esto, solo había probado C recientemente por primera vez. Me sorprendió gratamente cómo los lenguajes como C # y Java ocultan estos detalles en convenientes clases de biblioteca estándar como Diccionarios y Listas.
Soy consciente de que la biblioteca estándar de C ++ tiene muchos contenedores, como vectores, mapas y cadenas, y C ++ 11 solo se suma a esto al tener std :: array y bucles a distancia.
¿Cómo aprendo mejor el uso de estas características del lenguaje moderno y cuáles son adecuadas para cada momento? ¿Es correcto que la ingeniería de software en C ++ hoy en día esté mayormente libre de administración manual de memoria?
Por último, ¿qué compilador debo usar para aprovechar al máximo el nuevo estándar? Visual Studio tiene excelentes herramientas de depuración, pero incluso VS2012 parece tener una terrible compatibilidad con C ++ 11.
g++ -std=c++11
Respuestas:
Primero, algunas reglas generales:
Úselo
std::unique_ptr
como un puntero inteligente sin gastos generales. No debería tener que molestarse con punteros crudos con tanta frecuencia.std::shared_ptr
También es innecesario en la mayoría de los casos. Un deseo de propiedad compartida a menudo revela una falta de pensamiento sobre la propiedad en primer lugar.Úselo
std::array
para matrices de longitud estática ystd::vector
para dinámico.Use algoritmos genéricos ampliamente, en particular:
<algorithm>
<numeric>
<iterator>
<functional>
Uso
auto
ydecltype()
donde sea que beneficien la legibilidad. En particular, cuando se desea declarar una cosa, pero de un tipo que no se preocupan por como un iterador o tipo de plantilla compleja, el usoauto
. Cuando desee declarar una cosa en términos del tipo de otra cosa, usedecltype()
.Haga que las cosas sean seguras cuando sea posible. Cuando tienes afirmaciones que imponen invariantes en un tipo particular de cosas, esa lógica se puede centralizar en un tipo. Y esto no necesariamente implica una sobrecarga de tiempo de ejecución. También debería dejar de decir que los moldes de estilo C (
(T)x
) deben evitarse en favor de los moldes de estilo C ++ más explícitos (y buscables) (por ejemplo,static_cast
).Finalmente, sepa cómo la regla de tres:
Se ha convertido en la regla de cinco con la adición del constructor de movimiento y el operador de asignación de movimiento. Y entienda las referencias de valor en general y cómo evitar la copia.
C ++ es un lenguaje complejo, por lo que es difícil de caracterizar mejor manera de utilizar todos de la misma. Pero las prácticas de un buen desarrollo de C ++ no han cambiado fundamentalmente con C ++ 11. Debería preferir los contenedores administrados por memoria en lugar de la administración manual de memoria: los punteros inteligentes facilitan hacer esto de manera eficiente.
Yo diría que el C ++ moderno está en su mayoría libre de administración manual de memoria. La ventaja del modelo de memoria de C ++ es que es determinista , no manual. Las desasignaciones predecibles hacen que el rendimiento sea más predecible.
En cuanto a un compilador, G ++ y Clang son competitivos en términos de características de C ++ 11 y se están poniendo al día rápidamente en sus deficiencias. No uso Visual Studio, así que no puedo hablar a favor ni en contra.
Finalmente, una nota sobre
std::for_each
: evitarlo en general.transform
,accumulate
Yerase
-remove_if
son buenos edad funcionalmap
,fold
yfilter
. Perofor_each
es más general y, por lo tanto, menos significativo: no expresa ninguna otra intención que no sea bucle. Además de eso, se usa en las mismas situaciones que en el rangofor
, y es sintácticamente más pesado, incluso cuando se usa sin puntos. Considerar:fuente
std::for_each
, hubiera esperado que el rango basado en el bucle sea un mejor reemplazo que el simplefor
.std::for_each
, no con rango . Lo he eliminado para evitar confusiones.std::for_each()
. Cuando tienes lambda, sin duda es mejor que unfor
bucle tradicional . Con unfor
bucle basado en rango que podría no ser el caso, pero no escribió "for
bucle basado en rango ".for
bucle" incluye "for
bucle basado en rango ". He editado con más explicaciones y un ejemplo para aclarar, gracias.Como punto de partida:
char*
para cuerdas. Usestd::string
ostd::wstring
y solo observe cómo su código se acorta, se puede leer más y es más seguro[ ]
) y usestd::vector
o alguna otra clase de contenedor adecuada. Lo bueno de estostd::vector
es que conoce su propia longitud, limpia su contenido cuando está fuera de alcance, es fácil de iterar y se hace más grande cuando agrega más elementos. Pero hay otras colecciones que podrían funcionar aún mejor para sus circunstancias.std::unique_ptr
- y aprenderstd::move
casi de inmediato. Dado que esto puede dar lugar a algunos objetos noncopyable, la pereza puede enviarle ocasionalmente haciastd::shared_ptr
- y puede que tenga algunos casos de uso reales parastd::shared_ptr
asíauto
cuando declare iteradores y tipos que dependen de declaraciones anteriores (por ejemplo, antes declaró un vector de algo, ahora está declarando algo, useauto
)for_each
sobre un "raw for" siempre que pueda, ya que evita que otros lean su ciclo cuidadosamente para concluir que de hecho está iterando sobre toda la colección, etc. Si su compilador admite "range for", úselofor_each
. Aprender llamadas algoritmo triviales comoiota
,generate
,accumulate
,find_if
y así sucesivamente.No te preocupes demasiado sobre qué compilador usar. La falta "terrible, horrible" de compatibilidad con C ++ 11 en VS2012 es que no hay plantillas variadas (sí, claro, estabas a punto de usar plantillas variadas) y el
{}
inicializador no está allí. También quiero eso, pero difícilmente dejaré de usar una herramienta de desarrollo útil.La segunda cosa que hacer, después de abrazar
std::
, es comenzar a pensar en RAII. Cada vez que tienesEntonces, lo que tiene es un constructor, varias funciones miembro y un destructor. Escribe una clase que se encargue de eso por ti. Es posible que ni siquiera tenga que escribir el ctor y el dtor. Poner una
shared_ptr
variable como miembro de una clase es un ejemplo de RAII: no se escribe código de administración de memoria, pero cuando su instancia se salga del alcance, sucederán las cosas correctas. Expanda ese pensamiento para cubrir cosas como cerrar archivos, liberar manijas, bloqueos, etc. y el código se volverá más simple y más pequeño (al tiempo que elimina las fugas) ante sus ojos.Si se siente realmente seguro, purgue
printf
a favorcout
, elimine las macros (#define
cosas) y comience a aprender algunos "modismos avanzados" como PIMPL. Tengo un curso completo sobre esto en Pluralsight que probablemente puedas ver usando su versión de prueba gratuita.fuente
Por programación. La experiencia es la mejor manera de aprender.
C ++ 11 tiene muchas características nuevas (auto, rvalue, nuevos punteros inteligentes, solo por nombrar algunos). El mejor comienzo es comenzar a usarlos y leer sobre ellos siempre que pueda y cuando encuentre un artículo interesante.
Eso depende de lo que necesites hacer. La mayoría de las aplicaciones pueden escapar con punteros inteligentes y olvidarse de la administración de memoria. Todavía hay aplicaciones que no pueden escapar tan fácilmente (por ejemplo, si necesitan una ubicación nueva o un asignador de memoria personalizado por cualquier motivo).
Si necesita usar Qt, deberá usar sus reglas para la administración de la memoria.
Lo que tenga a la mano que admita el último estándar:
pero ningún compilador admite todas las funciones.
fuente
Mi universidad todavía usa C ++ para la enseñanza. He programado con C ++ durante 5 años y ahora soy un estudiante graduado. Por supuesto, ahora estoy usando mucho Java, Ruby, etc. Realmente recomiendo que no se apure demasiado con esas características en un lenguaje como Java. En mi experiencia y opinión, después de las características de bajo nivel de C ++. Debería examinar temas como la programación genérica con C / C ++, como hacer una clase de plantilla, funciones de plantilla, operador sobreescrito, métodos virtuales, punteros inteligentes. Para la Parte de diseño orientado a objetos, hay muchas características que C ++ tiene y Java no, como la herencia múltiple. La herencia es poderosa pero peligrosa también. El nivel de implementación del diseño orientado a objetos en C ++ también es un buen tema. La comunicación entre procesos, los subprocesos, también son importantes en C ++.
Para el compilador y depurador. Sé que Visual Studio es increíble. Pero realmente te recomiendo que aprendas GDB, Valgrind y Make still, y que seas bueno en estas herramientas. Microsoft es increíble, pero hizo muchas cosas por ti. Como estudiante, realmente necesitas aprender esas cosas que Microsoft también hizo contigo. Para el compilador, G ++ es bueno de GNU.
Después de todo, después de tantos años, realmente siento que las cosas más importantes son las características de bajo nivel como la matriz en bruto. Vector es realmente solo una matriz dinámica. Estas son mis recomendaciones, algo quizás demasiado subjetivo, solo toma lo que creas que es correcto.
fuente