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:
lock_mutex(&mutex);
// call some functions
...
unlock_mutex(&mutex);
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:
void f(const Foo* f1)
{
Foo f2;
memcpy(&f2, f1, sizeof f2);
...
}
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 como memset
, 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):
memcpy(&f2, f1, sizeof f2);
también es "el caos reinante del fuego del infierno" en C si Foo tiene punteros propios, o es aún peor, ya que también te faltan las herramientas para lidiar con eso.
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 los qsort
tipos de funciones representan un peligro menos práctico en C es que la destrucción de f1
y por f2
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. Si f1
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
o realloc
).
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
y memmove
y qsort
y memset
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