Certaines fonctionnalités du langage, même s'il s'agit d'ajouts, peuvent changer la façon dont la langue doit être utilisée. À titre d'exemple, considérons ce cas:
lock_mutex(&mutex);
// call some functions
...
unlock_mutex(&mutex);
Si ce code ci-dessus impliquait d'appeler des fonctions implémentées en C ++, nous pourrions être dans un monde de problèmes, car n'importe lequel de ces appels de fonction peut lancer et nous ne déverrouillerons jamais le mutex dans ces chemins exceptionnels.
Les destructeurs ne sont plus dans le domaine de la commodité pour aider les programmeurs à éviter d'oublier de libérer / libérer des ressources à ce stade. RAII devient une exigence pratique car il n'est pas humainement possible d'anticiper chaque ligne de code qui peut lancer des exemples non triviaux (sans mentionner que ces lignes peuvent ne pas lancer maintenant mais peuvent plus tard avec des modifications). Prenons un autre exemple:
void f(const Foo* f1)
{
Foo f2;
memcpy(&f2, f1, sizeof f2);
...
}
Un tel code, bien que généralement inoffensif en C, est comme un enfer qui fait des ravages en C ++, car les memcpy
bulldozers sur les bits et octets de ces objets et contournent des choses comme les constructeurs de copie. De telles fonctions telles que memset
, realloc
, memcpy
, etc., tandis que les outils quotidiens entre les développeurs C utilisés pour regarder les choses d'une manière assez homogène de bits et d' octets dans la mémoire, ne sont pas en harmonie avec le système de type plus complexe et plus riche de C ++. C ++ encourage une vue beaucoup plus abstraite des types définis par l'utilisateur.
Donc, ces types de choses ne permettent plus au C ++, par quiconque cherche à l'utiliser correctement, de le considérer comme un simple "sur-ensemble" de C. Ces langages nécessitent un état d'esprit, une discipline et une façon de penser très différents pour être utilisés le plus efficacement possible. .
Je ne suis pas dans le camp qui voit le C ++ comme carrément meilleur à tous égards, et en fait la plupart de mes bibliothèques tierces préférées sont des bibliothèques C pour une raison quelconque. Je ne sais pas pourquoi exactement, mais les bibliothèques C ont tendance à être de nature plus minimaliste (peut-être parce que l'absence d'un système de type aussi riche rend les développeurs plus concentrés sur la fourniture des fonctionnalités minimales requises sans construire un ensemble d'abstractions volumineux et en couches), bien que je finisse souvent par mettre des wrappers C ++ autour d'eux pour simplifier et adapter leur utilisation à mes besoins, mais cette nature minimaliste est préférable pour moi même lorsque je fais cela. J'aime vraiment le minimalisme comme caractéristique attrayante d'une bibliothèque pour ceux qui prennent le temps supplémentaire de rechercher de telles qualités, et peut-être que C a tendance à encourager cela,
Je préfère C ++ beaucoup plus souvent qu'autrement, mais je suis en fait obligé d'utiliser les API C assez souvent pour la compatibilité binaire la plus large (et pour les FFI), bien que je les implémente souvent en C ++ malgré l'utilisation de C pour les en-têtes. Mais parfois, quand vous allez vraiment bas niveau, comme au niveau d'un allocateur de mémoire ou d'une structure de données très bas niveau (et je suis sûr qu'il y a d'autres exemples parmi ceux qui font de la programmation embarquée), il peut parfois être utile d'être capable de supposer que les types et les données avec lesquels vous travaillez sont absents de certaines fonctionnalités comme les vtables, les costructors et les destructeurs, afin que nous puissions les traiter comme des bits et des octets à mélanger, copier, libérer, réallouer. Pour des problèmes très bas niveau, il peut parfois être utile de travailler avec un système de type beaucoup plus simple que C fournit,
Une clarification
Un commentaire intéressant ici, je voulais répondre un peu plus en profondeur (je trouve que les commentaires ici sont si stricts sur la limite de caractères):
memcpy(&f2, f1, sizeof f2);
est également «un enfer qui fait des ravages» en C si Foo a des pointeurs propriétaires, ou pire encore, car vous n'avez pas non plus les outils pour y faire face.
C'est un bon point, mais tout ce sur quoi je me concentre est principalement axé sur le système de type C ++ et également sur RAII. L'une des raisons pour lesquelles la copie d'octets aux rayons X memcpy
ou les qsort
types de fonctions présentent moins de danger pratique en C est que la destruction de f1
et f2
au-dessus est explicite (si elle a même besoin d'une destruction non triviale), alors que lorsque les destructeurs entrent dans l'image , ils deviennent implicites et automatisés (souvent avec une grande valeur pour les développeurs). Cela ne veut même pas mentionner l'état caché comme les vptrs, etc. Si f1
possède des pointeurs etf2
shallow les copie dans un contexte temporaire, cela ne pose aucun problème si nous n'essayons pas de libérer explicitement ces pointeurs propriétaires une deuxième fois. Avec C ++, c'est quelque chose que le compilateur voudra automatiquement faire.
Et cela devient plus gros si généralement en C, " Si Foo a des pointeurs propriétaires", car l'explicitation requise avec la gestion des ressources rendra souvent quelque chose de plus difficile à ignorer alors qu'en C ++, nous pouvons faire un UDT plus trivialement constructible / destructible en lui faisant simplement stocker toute variable membre qui n'est pas trivialement constructible / destructible (d'une manière qui est généralement très utile, encore une fois, mais pas si nous sommes tentés d'utiliser des fonctions comme memcpy
ou realloc
).
Mon point principal n'est pas d'essayer de faire valoir un quelconque avantage de cette explicitation (je dirais que s'il y en a, ils sont presque toujours alourdis par les inconvénients de la probabilité accrue d'erreur humaine qui l'accompagne), mais simplement de dire que des fonctions comme memcpy
et memmove
et qsort
et memset
etrealloc
et ainsi de suite n'ont pas leur place dans un langage avec des UDT aussi riches en fonctionnalités et capacités que C ++. Bien qu'ils existent malgré tout, je pense qu'il ne serait pas trop contesté de dire que la grande, grande majorité des développeurs C ++ serait sage d'éviter de telles fonctions comme la peste, alors que ce sont des types très quotidiens de fonctions en C, et je '' d soutiennent qu'ils posent moins de problèmes en C pour la simple raison que son système de type est beaucoup plus basique et peut-être "plus bête". La radiographie des types C et leur traitement comme des bits et des octets sont sujets aux erreurs. Faire cela en C ++ est sans doute tout simplement erroné car de telles fonctions se battent contre des caractéristiques très fondamentales du langage et ce qu'il encourage du système de type.
C'est en fait le plus grand attrait pour moi de C, cependant, en particulier avec sa relation avec l'interopérabilité linguistique. Il serait beaucoup, beaucoup plus difficile de faire comprendre à quelque chose comme le FFI de C # le système de type complet et les fonctionnalités du langage C ++ jusqu'aux constructeurs, destructeurs, exceptions, fonctions virtuelles, surcharge de fonction / méthode, surcharge d'opérateur, tous les différents types de l'héritage, etc. Avec C, c'est un langage relativement plus bête qui est devenu plutôt standard en ce qui concerne les API de manières que de nombreux langages différents peuvent importer directement via les FFI, ou indirectement via certaines fonctions d'exportation d'API C sous la forme souhaitée (ex: Java Native Interface ). Et c'est là que je n'ai pratiquement pas d'autre choix que d'utiliser C, car l'interopérabilité du langage est une exigence pratique dans notre cas (bien que souvent je ''
Mais vous savez, je suis pragmatique (ou du moins je m'efforce de l'être). Si C était ce langage le plus grossier et le plus véreux, sujet aux erreurs et méchant, certains de mes pairs passionnés de C ++ le prétendaient (et je me considérerais comme un passionné de C ++, sauf que d'une manière ou d'une autre, cela n'a pas conduit à une haine de C de ma part ; au contraire, cela a eu l'effet inverse sur moi de me faire mieux apprécier les deux langues à leurs propres égards et différences), alors je m'attendrais à ce que cela apparaisse dans le monde réel sous la forme de certains des plus buggés et des plus fuyants et des produits et des bibliothèques peu fiables étant écrits en C. Et je ne trouve pas cela. J'aime Linux, j'aime Apache, Lua, zlib, je trouve OpenGL tolérable pour son long héritage contre de telles exigences matérielles changeantes, Gimp, libpng, Cairo, etc. Au moins, quels que soient les obstacles posés par la langue, cela ne semble pas constituer une impasse en ce qui concerne l'écriture de bibliothèques et de produits sympas entre des mains compétentes, et c'est vraiment tout ce qui m'intéresse. les guerres de langues, sauf pour lancer un appel pragmatique et dire: «Hé, il y a des trucs sympas là-bas! quelle que soit la ou les langues que nous utilisons. " :-RÉ