Muchas personas han dicho que C ++ es un lenguaje completamente diferente que C, pero el propio Bjarne ha dicho que C ++ es un lenguaje que se extiende desde C, de ahí que es de donde ++
proviene. Entonces, ¿por qué todos dicen que C y C ++ son lenguajes completamente diferentes? ¿En qué se diferencia C de C ++ de las características extendidas en C ++?
programming-languages
c++
c
Joshua Partogi
fuente
fuente
Respuestas:
Durante la década de 1980, cuando el desarrollo de C ++ apenas comenzaba, C ++ era casi un superconjunto adecuado de C. Así es como comenzó todo.
Sin embargo, con el tiempo, tanto C como C ++ evolucionaron y se han separado entre sí, aunque la compatibilidad entre los lenguajes siempre se ha considerado importante.
Además, las diferencias técnicas entre C y C ++ han hecho que las expresiones idiomáticas típicas en esos lenguajes y lo que se considera 'buena práctica' diverjan aún más.
Este es el factor determinante detrás de las personas que dicen cosas como "no existe un lenguaje como C / C ++" o "C y C ++ son dos lenguajes diferentes". Aunque es posible escribir programas que sean aceptables tanto para un compilador C como para C ++, el código generalmente no se considera un ejemplo de buen código C ni un buen código C ++.
fuente
void *
pero C ++ nunca lo hizo. (No votó a favor)Stroustrup mismo responde eso en sus preguntas frecuentes :
Es compatible con la programación orientada a objetos y la programación genérica que hacen que C ++ sea "completamente diferente" a C. Casi puede escribir C puro y luego compilarlo con un compilador de C ++ (siempre y cuando se encargue de la verificación de tipos más estricta). Pero aún estás escribiendo C, no estás escribiendo C ++.
Si está escribiendo C ++, está haciendo uso de sus características orientadas a objetos y de plantilla, y eso no se parece en nada a lo que vería en C.
fuente
En pocas palabras, lo que se considera idiomático en C definitivamente no lo es en C ++.
C y C ++ son lenguajes muy diferentes en la práctica, debido a la forma en que las personas los usan. C apunta al minimalismo, donde C ++ es un lenguaje muy complejo, con muchas características.
También hay algunas diferencias prácticas: se puede llamar fácilmente a C desde prácticamente cualquier lenguaje, y a menudo define el ABI de una plataforma, mientras que C ++ es bastante difícil de usar desde otras bibliotecas. La mayoría de los lenguajes tienen una FFI o interfaz en C, incluso los lenguajes implementados en C ++ (Java, por ejemplo).
fuente
Además del hecho obvio de que C ++ admite programación orientada a objetos, creo que tiene su respuesta aquí: http://en.wikipedia.org/wiki/Compatibility_of_C_and_C++
Ese artículo contiene ejemplos de código que muestran cosas que están bien en C pero no en C ++. Por ejemplo:
Portar un programa C a C ++ a menudo es sencillo y consiste principalmente en corregir errores de compilación (agregar conversiones, nuevas palabras clave, etc.).
fuente
C ++ agrega no solo nuevas características, sino también nuevos conceptos y nuevas expresiones idiomáticas a C. Aunque C ++ y C están estrechamente relacionados, el hecho es que para escribir efectivamente en un lenguaje, debe pensar en el estilo de ese lenguaje. Incluso el mejor código C no puede aprovechar las diferentes fortalezas y modismos de C ++, por lo que es más probable que no sea un código C ++ bastante malo .
fuente
Las "características extendidas", hacen que parezca que en C ++ agregaron macros variadic o algo así, y eso es todo. Las "características extendidas" en C ++ son una revisión completa del lenguaje y reemplazan totalmente las mejores prácticas de C porque las nuevas características de C ++ son mucho mejores que las características de C originales que las características de C originales son completamente y totalmente redundantes en la gran mayoría de los casos . Sugerir que C ++ simplemente extiende C sugiere que un tanque de batalla moderno extiende un cuchillo de mantequilla con el propósito de librar una guerra.
fuente
La diferencia es que en C piensa de manera procesal y en C ++ piensa de forma orientada a objetos. Los idiomas son bastante similares, pero el enfoque es muy diferente.
fuente
Si bien C ++ puede ser un superconjunto de C en términos sintácticos, es decir, cualquier compilación del programa C puede ser compilada por el compilador de C ++.
Sin embargo, casi nunca escribe un programa C ++ de la misma manera que lo hubiera hecho con el programa C. La lista puede ser interminable o puede ser que alguien deba investigar más para ponerla como informes exhaustivos. Sin embargo, pongo algunos indicadores que marcan las diferencias clave.
El punto de la publicación actual es que C ++ tiene las siguientes características que los buenos programadores de C ++ deben usar como mejores prácticas de programación, aunque es posible compilar C equivalente.
¿Cómo se debe hacer en C ++ sobre C
Clases y herencia. Esas son las diferencias más importantes que permiten la orientación sistemática de objetos que hace que la expresión de programación sea muy poderosa. Supongo que este punto no necesita una mejor explicación. Si está en C ++, casi siempre, es mejor hacer uso de las clases.
Privatización: las clases e incluso las estructuras tienen miembros privados. Esto hace posible la encapsulación de una clase. El equivalente en C es encasillando el objeto como vacío * a la aplicación para que la aplicación no tenga acceso a variables internas. Sin embargo, en C ++ puede tener elementos con clases públicas y privadas.
Pase por referencia. C ++ permite la modificación basada en la referencia, para lo cual se requieren punteros de paso. El paso por referencia mantiene el código muy limpio y más seguro contra los riesgos de puntero. También puede pasar el puntero de estilo C y eso funciona, pero si está en C ++, estará mejor siempre que
nuevo y eliminar vs malloc y gratis. Las declaraciones new () y delete () no solo asignan y desasignan memoria, sino que también permiten que el código se ejecute como parte de destruct-er para ser llamado en una cadena. Si está usando C ++, en realidad es MALO usar malloc y gratis.
Tipos de E / S y sobrecarga del operador La sobrecarga del operador hace que el código sea legible o más intuitivo si se hace bien. Lo mismo para los operadores << y >> io. La forma en C de hacer esto sería usar punteros de función, pero es complicado y solo para programadores avanzados.
Usando "cadena". El char * de C funciona en todas partes. Entonces, C y C ++ son más o menos lo mismo. Sin embargo, si está en C ++, siempre es mucho mejor (y más seguro) usar las clases de String, lo que lo salva de los peligros de las matrices sobre la ejecución, que son casi todas las cosas.
Características de las que aún no sería fanático en C ++ 1. Plantillas: aunque no uso plantillas pesadas en muchos códigos, puede resultar muy potente para las bibliotecas. Casi no hay un equivalente en C. Pero en un día normal, especialmente si te falta matemáticamente.
Cosas que me gustan de C y extraño en C ++
Algoritmos polimórficos mediante el uso de punteros de función. En C, cuando ejecuta algoritmos complejos, algunas veces puede usar un conjunto de punteros de función. Esto hace verdadero polimorfismo de una manera poderosa. Cuando está en C ++, PUEDE usar punteros de función, pero eso es malo. Debería usar solo métodos; de lo contrario, prepárese para ponerse desordenado. La única forma de polimorfismo en las clases de C ++ es la sobrecarga de funciones y operadores, pero eso es bastante limitante.
Hilos simples Cuando creas hilos eran pthreads, es bastante simple y manejable. Se convierte en cuando necesita crear hilos que se supone que son "privados" para las clases (para que tengan acceso a miembros privados). Hay marcos de tipo impulso , pero nada en C ++ básico.
Dipan
fuente
Ciertas características del lenguaje, incluso si son adiciones, pueden cambiar la forma en que el lenguaje prácticamente necesita ser utilizado. Como un ejemplo, considere este caso:
Si el código anterior involucraba funciones de llamada implementadas en C ++, podríamos estar en un mundo de problemas, ya que cualquiera de esas llamadas de función puede lanzarse y nunca desbloquearemos el mutex en esas rutas excepcionales.
Los destructores ya no son convenientes para ayudar a los programadores a evitar olvidar liberar / liberar recursos en ese momento. RAII se convierte en un requisito práctico porque no es humanamente factible anticipar cada línea de código que pueda incluir ejemplos no triviales (sin mencionar que esas líneas pueden no arrojarse ahora, pero pueden cambiar más adelante). Toma otro ejemplo:
Tal código, aunque generalmente es inocuo en C, es como el caos del infierno que reina en C ++, porque las
memcpy
excavaciones sobre los bits y bytes de estos objetos y evita cosas como los constructores de copias. Tales funciones comomemset
,realloc
,memcpy
, etc, mientras que las herramientas diarias entre los desarrolladores de C utilizado para ver las cosas de una manera bastante homogénea de bits y bytes en la memoria, no están en armonía con el sistema de tipo más complejo y más rico de C ++. C ++ fomenta una visión mucho más abstracta de los tipos definidos por el usuario.Entonces, este tipo de cosas ya no permiten que C ++, para cualquiera que quiera usarlo correctamente, lo vea como un mero "superconjunto" de C. Estos lenguajes requieren una mentalidad, disciplina y forma de pensar muy diferentes para usarlo de manera más efectiva .
No estoy en el campo que ve a C ++ como absolutamente mejor en todos los sentidos, y en realidad la mayoría de mis bibliotecas de terceros favoritas son bibliotecas de C por alguna razón. No sé exactamente por qué, pero las bibliotecas C tienden a ser de naturaleza más minimalista (tal vez porque la ausencia de un sistema de tipo tan rico hace que los desarrolladores se centren más en proporcionar la funcionalidad mínima requerida sin crear un conjunto de abstracciones grandes y en capas), aunque a menudo termino simplemente colocando envoltorios de C ++ a su alrededor para simplificar y adaptar su uso para mis propósitos, pero esa naturaleza minimalista es preferible para mí incluso cuando lo hago. Realmente amo el minimalismo como una característica atractiva de una biblioteca para aquellos que se toman el tiempo extra para buscar tales cualidades, y tal vez C tiende a alentar eso,
Estoy a favor de C ++ con mucha frecuencia, pero en realidad se me exige que use las API de C con bastante frecuencia para la compatibilidad binaria más amplia (y para FFI), aunque a menudo las implemento en C ++ a pesar de usar C para los encabezados. Pero a veces, cuando se pasa a un nivel realmente bajo, como el nivel de un asignador de memoria o una estructura de datos de muy bajo nivel (y estoy seguro de que hay más ejemplos entre los que hacen programación integrada), a veces puede ser útil ser capaz de asumir que los tipos y datos con los que está trabajando están ausentes de ciertas características como vtables, costructores y destructores, por lo que podemos tratarlos como bits y bytes para barajar, copiar, liberar, reasignar. Para problemas de muy bajo nivel, a veces puede ser útil trabajar con un sistema de tipo mucho más simple que proporciona C,
Una clarificación
Un comentario interesante aquí que quería responder un poco más en profundidad (los comentarios aquí son muy estrictos en cuanto al límite de caracteres):
Ese es un punto justo, pero todo en lo que me estoy centrando es predominantemente con un enfoque en el sistema de tipos de C ++ y también con respecto a RAII. Una de las razones por las cuales la copia de bytes en rayos X
memcpy
o losqsort
tipos de funciones representan un peligro menos práctico en C es que la destrucción def1
y porf2
encima es explícita (si incluso necesitan una destrucción no trivial), mientras que cuando los destructores entran en escena , se vuelven implícitos y automatizados (a menudo con un gran valor para los desarrolladores). Eso ni siquiera menciona el estado oculto como vptrs, etc., sobre el cual tales funciones se demorarían. Sif1
posee punteros yf2
Shallow los copia en algún contexto temporal, entonces no plantea ningún problema si no intentamos liberar explícitamente a los propietarios de punteros por segunda vez. Con C ++ eso es algo que el compilador querrá hacer automáticamente.Y eso se vuelve más grande si normalmente está en C, " Si Foo tiene punteros propietarios", porque la explicitación requerida con la gestión de recursos a menudo hará que algo sea típicamente más difícil de pasar por alto, mientras que en C ++, podemos hacer que un UDT ya no sea trivial constructible / destructible simplemente haciendo que almacene cualquier variable miembro que no sea trivialmente constructible / destructible (de una manera que generalmente es muy útil, de nuevo, pero no si estamos tentados a usar funciones como
memcpy
orealloc
).Mi punto principal no es tratar de argumentar ningún beneficio de esta explicidad (yo diría que si hay alguna, casi siempre están agobiadas por las desventajas de la mayor probabilidad de error humano que conlleva), sino simplemente decir que funciones como
memcpy
ymemmove
yqsort
ymemset
yrealloc
y así sucesivamente no tienen cabida en un lenguaje con UDT tan rico en características y capacidades como C ++. Si bien existen independientemente, creo que no sería demasiado discutible decir que la gran mayoría de los desarrolladores de C ++ sería prudente evitar tales funciones como la peste, mientras que estas son funciones muy cotidianas en C, y yo ' Argumentaría que plantean menos problemas en C por la simple razón de que su sistema de tipos es mucho más básico y, quizás, "más tonto". Hacer rayos X con los tipos C y tratarlos como bits y bytes es propenso a errores. Hacer eso en C ++ podría decirse que es completamente erróneo porque tales funciones están luchando contra características muy fundamentales del lenguaje y lo que alienta del sistema de tipos.Sin embargo, ese es realmente el mayor atractivo para mí de C, específicamente con la forma en que se relaciona con la interoperabilidad del lenguaje. Sería mucho, mucho más difícil hacer que algo como FFI de C # entienda el sistema de tipos completo y las características del lenguaje de C ++ hasta constructores, destructores, excepciones, funciones virtuales, sobrecarga de funciones / métodos, sobrecarga de operadores, todos los diversos tipos de herencia, etc. Con C es un lenguaje relativamente más tonto que se ha vuelto bastante estándar en cuanto a las API de manera que muchos lenguajes diferentes pueden importar directamente a través de FFI, o indirectamente a través de algunas funciones de exportación de API de C en la forma deseada (por ejemplo: Java Native Interface ) Y ahí es donde principalmente no me queda más remedio que usar C, ya que la interoperabilidad del lenguaje es un requisito práctico en nuestro caso (aunque a menudo yo '
Pero ya sabes, soy un pragmático (o al menos me esfuerzo por serlo). Si C fuera este lenguaje desagradable y propenso, propenso a errores y desagradable, algunos de mis pares entusiastas de C ++ afirmaron que era (y me consideraría un entusiasta de C ++, excepto que de alguna manera no ha llevado a un odio hacia C por mi parte ; por el contrario, tuvo el efecto opuesto en mí al hacerme apreciar ambos idiomas mejor en sus propios aspectos y diferencias), entonces esperaría que se mostrara en el mundo real en la forma de algunos de los errores y goteos productos y bibliotecas poco confiables que se escriben en C. Y no encuentro eso. Me gusta Linux, me gusta Apache, Lua, zlib, encuentro OpenGL tolerable por su largo legado frente a los requisitos de hardware tan cambiantes, Gimp, libpng, Cairo, etc. Al menos cualquier obstáculo que el lenguaje plantea no parece representar un punto muerto en cuanto a escribir algunas bibliotecas y productos geniales en manos competentes, y eso es realmente todo lo que me interesa. Así que nunca he sido del tipo tan interesado en los más apasionados guerras lingüísticas, excepto para hacer un llamamiento pragmático y decir: "¡Oye, hay cosas geniales ahí fuera! Aprendamos cómo lo hicieron y tal vez hay lecciones interesantes, no tan específicas de la naturaleza idiomática del idioma, que podemos traer de vuelta a cualquier idioma que estemos usando ". :-RE
fuente
memcpy(&f2, f1, sizeof f2);
también es "caos reinante del infierno" en C siFoo
tiene algún puntero propio, o es aún peor, ya que también carece de las herramientas para lidiar con eso. Entonces, las personas que escriben C no hacen tantas cosas asíFoo
instancias ahora pueden querer destruir la matriz dinámica, o vector, o algo por el estilo. A menudo encuentro para algunos casos peculiares que es útil poder escribir ciertas estructuras de datos para apoyar el hecho de que la destrucción es explícita en lugar de implícita.Foo
tiene punteros propietarios, podríamos darle una copia / movimiento apropiado, dtor, usar semántica de valor, etc., y tener un código mucho más rico y seguro. Pero ocasionalmente me encuentro buscando C en los casos en que quiero generalizar una estructura de datos o un asignador en el nivel homogéneo de bits y bytes, con ciertas suposiciones que puedo hacer sobre los tipos que tienden a simplificarse en sus casos de uso estrechos. un poco por tales suposiciones me siento un poco más seguro de hacer en C.API void filter_image(byte* pixels, int w, int h);
como un simple ejemplo en lugar de me gustaAPI void filter_image(ImageInterface& img);
(que acoplaría nuestro código a dicha interfaz de imagen, estrechando su aplicabilidad). En tales casos, a veces solo implemento tales funciones en C, ya que hay poco que ganar en C ++, y reduce la probabilidad de que dicho código necesite cambios futuros.