En quoi le C est-il différent du C ++?


21

Beaucoup de gens ont dit que C ++ est un langage complètement différent de C, mais Bjarne lui-même a dit que C ++ est un langage qui est étendu à partir de C, d'où son origine ++. Alors pourquoi tout le monde continue de dire que C et C ++ sont des langages complètement différents? En quoi le C est-il différent du C ++ à part les fonctionnalités étendues du C ++?


3
En raison de la façon dont ils sont utilisés. Vous pouvez certainement écrire C en C ++ ... mais vous ne devriez pas.
Ed S.

Réponses:


18

Pendant les années 80, alors que le développement de C ++ commençait à peine, C ++ était presque un surensemble approprié de C. C'est ainsi que tout a commencé.
Cependant, au fil du temps, le C et le C ++ ont évolué et ont divergé l'un de l'autre, même si la compatibilité entre les langages a toujours été considérée comme importante.

De plus, les différences techniques entre C et C ++ ont rendu les idiomes typiques de ces langages et ce qui est considéré comme une «bonne pratique» divergent encore plus.

C'est le facteur qui motive les gens à dire des choses comme «il n'y a pas de langage comme C / C ++» ou «C et C ++ sont deux langages différents». Bien qu'il soit possible d'écrire des programmes acceptables à la fois pour un compilateur C et C ++, le code n'est généralement considéré ni comme un exemple de bon code C ni comme un exemple de bon code C ++.


Je ne pense pas que le premier paragraphe soit correct. Je crois que C a toujours eu un casting implicite, void *mais C ++ ne l'a jamais fait. (N'a pas downvote)
alternative

5
@mathepic: Définissez "toujours". C a pris le type void * de C ++. Dans K&R C, malloc revenait char *
Nemanja Trifunovic

19

Stroustrup lui-même répond à cela dans sa FAQ :

C ++ est un descendant direct de C qui conserve presque tout C comme sous-ensemble. C ++ fournit une vérification de type plus forte que C et prend directement en charge une plus large gamme de styles de programmation que C. C ++ est "un meilleur C" dans le sens où il prend en charge les styles de programmation effectués à l'aide de C avec une meilleure vérification de type et une prise en charge plus notationnelle (sans perte) d’efficacité). Dans le même sens, ANSI C est un meilleur C que K&R C. En outre, C ++ prend en charge l'abstraction des données, la programmation orientée objet et la programmation générique.

Il prend en charge la programmation orientée objet et la programmation générique qui rendent le C ++ "complètement différent" du C. Vous pouvez presque écrire du C pur puis le compiler avec un compilateur C ++ (tant que vous vous occupez de la vérification de type plus stricte). Mais alors vous écrivez toujours du C - vous n'écrivez pas du C ++.

Si vous écrivez C ++, vous utilisez ses fonctionnalités orientées objet et modèle et cela ne ressemble en rien à ce que vous verriez en C.


"cela ne ressemble en rien à ce que vous verriez en C." Cette phrase semble drôle! "voir en C". C en C! C'est une sorte de poésie.
Galaxy

13

En termes simples, ce qui est considéré comme idiomatique en C n'est certainement pas idiomatique en C ++.

C et C ++ sont des langages très différents dans la pratique, en raison de la façon dont les gens les utilisent. C vise le minimalisme, où C ++ est un langage très complexe, avec beaucoup de fonctionnalités.

Il existe également des différences pratiques: le C peut être facilement appelé à partir de pratiquement n'importe quel langage et définit souvent l'ABI d'une plate-forme, tandis que le C ++ est assez difficile à utiliser à partir d'autres bibliothèques. La plupart des langages ont un FFI ou une interface en C, même les langages implémentés en C ++ (java, par exemple).


4
Ce n'est pas seulement que le code de style C n'est pas idiomatique en C ++. Le codage de style C est en fait problématique en C ++, en raison du manque de sécurité des exceptions.
dan04

4

Outre le fait évident que C ++ prend en charge la programmation orientée objet, je pense que vous avez votre réponse ici: http://en.wikipedia.org/wiki/Compatibility_of_C_and_C++

Cet article contient des exemples de code montrant des choses qui sont correctes en C mais pas en C ++. Par exemple:

int *j = malloc(sizeof(int) * 5); /* Implicit conversion from void* to int* */

Le portage d'un programme C vers C ++ est souvent simple et consiste principalement à corriger les erreurs de compilation (ajout de cast, nouveaux mots-clés, etc.).


4
Porter un programme comme ça ne vous donne pas un programme C ++. Il vous donne C qui peut être compilé sur le compilateur C ++. Cela ne fait pas C ++ (j'appellerais quand même le code résultant C (pas même C avec des classes)).
Martin York

@MartinYork Appelez ça du mauvais C, faites remarquer que la sémantique pourrait être différente, et je suis d'accord. Le résultat est évidemment C cependant.
Déduplicateur

2

C ++ ajoute non seulement de nouvelles fonctionnalités, mais de nouveaux concepts et de nouveaux idiomes à C.Même si C ++ et C sont étroitement liés, le fait demeure que pour écrire efficacement dans un langage, vous devez penser dans le style de ce langage. Même le meilleur code C ne peut pas tirer parti des différentes forces et idiomes du C ++, et est donc plus probable qu'improbable en fait un code C ++ plutôt mauvais .


2

Les "fonctionnalités étendues", vous le faites sonner comme en C ++, ils ont ajouté des macros variadiques ou quelque chose comme ça. Les «fonctionnalités étendues» en C ++ sont une refonte complète du langage et remplacent totalement les meilleures pratiques C car les nouvelles fonctionnalités C ++ sont tellement meilleures que les fonctionnalités C originales que les fonctionnalités C originales sont complètement et totalement redondantes dans la grande majorité des cas. . Suggérer que C ++ étend simplement C signifie qu'un char de combat moderne étend un couteau à beurre aux fins de faire la guerre.


Pouvez-vous énumérer un exemple de l'une des fonctionnalités les plus utiles du C ++ que le C n'a pas?
Dark Templar

2
@DarkTemplar: Que diriez-vous de la gestion des ressources en mode facile avec RAII? Ou de belles structures de données génériques utilisant des modèles? Pour commencer.
DeadMG

1

La différence est qu'en C vous pensez de manière procédurale et en C ++ vous pensez d'une manière orientée objet. Les langues sont assez similaires mais l'approche est très différente.


C n'a-t-il pas aussi des structures? Les fichiers C ne sont-ils pas eux-mêmes des modules séparés (essentiellement des objets)? Je ne sais pas quelle est la différence exacte entre procédural et orienté objet ...
Dark Templar

1
Sombre, vous avez illustré mon propos. Ce n'est pas une limitation du langage qui vous permet d'écrire de manière procédurale ou orientée objet, c'est une philosophie ou une façon de penser.
Ant

0

Alors que C ++ peut être un super ensemble de C en termes syntaxiques - c'est-à-dire que toute construction de programme C peut être compilée par le compilateur C ++.

Cependant, vous n'écrivez presque jamais un programme C ++ comme vous l'auriez fait avec un programme C. La liste peut être infinie ou peut-être que quelqu'un devrait simplement faire plus de recherches pour la mettre sous forme de rapports exhaustifs. Cependant, je mets quelques pointeurs qui font les principales différences.

Le point de l'article actuel est que C ++ a les fonctionnalités suivantes qu'un bon programmeur C ++ doit utiliser comme bonnes pratiques de programmation même s'il est possible de compiler l'équivalent C.

Comment cela devrait-il être fait en C ++ sur C

  1. Classes et héritage. Ce sont les différences les plus importantes qui permettent une orientation d'objet systématique qui rend l'expression de programmation très puissante. Je suppose - ce point n'a pas besoin d'une meilleure explication. Si vous êtes en C ++ - presque toujours, il vaut mieux utiliser des classes.

  2. Privatisation - Les classes et même les structures ont des membres privés. Cela rend possible l'encapsulation d'une classe. L'équivalent en C consiste à transtyper l'objet en tant que void * vers l'application afin que l'application n'ait pas accès aux variables internes. Cependant, en C ++, vous pouvez avoir des éléments avec des classes publiques et privées.

  3. Passez par référence. C ++ permet une modification basée sur la référence, pour laquelle le passage de pointeurs est requis. Le passage par référence maintient le code très propre et plus sûr contre les dangers du pointeur. Vous pouvez également passer un pointeur de style C et cela fonctionne - mais si vous êtes en C ++, vous êtes mieux tant que

  4. nouveau et supprimer vs malloc et gratuit. Les instructions new () et delete () non seulement allouent et désallouent de la mémoire, mais permettent également au code de s'exécuter dans le cadre du destructeur à appeler dans une chaîne. Si vous utilisez C ++ - il est en fait MAUVAIS d'utiliser malloc et gratuit.

  5. Types d'E / S et surcharge d'opérateur La surcharge d'opérateur rend le code lisible ou plus intuitif s'il est bien fait. Idem pour les opérateurs << et >> io. La façon C de le faire serait d'utiliser des pointeurs de fonction - mais c'est compliqué et uniquement pour les programmeurs avancés.

  6. Utilisation de "chaîne". Le caractère * de C fonctionne partout. Donc C et C ++ sont à peu près les mêmes. Cependant, si vous êtes en C ++ - il est toujours préférable (et plus sûr) d'utiliser des classes String qui vous évitent les dangers des tableaux sur l'exécution qui sont presque toutes choses.

Fonctionnalités dont je ne serais toujours pas fan en C ++ 1. Modèles - Bien que je n'utilise pas de modèles lourds dans de nombreux codes - cela peut s'avérer très puissant pour les bibliothèques. Il n'y a presque pas d'équivalent en C. Mais un jour normal - spécialement si vous manquez mathématiquement.

  1. Pointeurs intelligents - Oui, ils sont très intelligents! Et comme la plupart des choses intelligentes - elles commencent bien et deviennent désordonnées plus tard! Je n'aime pas trop utiliser

Les choses que j'aime dans C et qui manquent en C ++

  1. Algorithmes polymorphes utilisant des pointeurs de fonction. En C, lorsque vous exécutez des algorithmes complexes - vous pouvez parfois utiliser un ensemble de pointeurs de fonction. Cela rend le vrai polymorphisme d'une manière puissante. Lorsque vous êtes en C ++, vous POUVEZ utiliser des pointeurs de fonction - mais c'est mauvais. Vous ne devriez utiliser que des méthodes - sinon soyez prêt à vous salir. La seule forme de polymorphisme dans les classes C ++ est la surcharge de fonctions et d'opérateurs mais c'est assez limitant.

  2. Fils simples. Lorsque vous créez des threads étaient des pthreads - c'est assez simple et gérable. Cela se produit lorsque vous devez créer des threads qui sont censés être "privés" pour les classes (afin qu'ils aient accès à des membres privés). Il existe un type de framework boost - mais rien en C ++ de base.

Dipan.


0

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 memcpybulldozers 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 memcpyou les qsorttypes de fonctions présentent moins de danger pratique en C est que la destruction de f1et f2au-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 f1possède des pointeurs etf2shallow 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 memcpyou 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 memcpyet memmoveet qsortet memsetetreallocet 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É


2
memcpy(&f2, f1, sizeof f2);est aussi « hellfire régnante des ravages » dans C si Fooa des pointeurs propriétaires, ou est encore pire, comme vous aussi manque les outils nécessaires pour faire face à cela. Donc les gens qui écrivent C ne font pas autant de choses comme ça
Caleth

@Caleth Une partie de ce qui simplifie le fait que même avec des pointeurs propriétaires est la libération explicite des pointeurs propriétaires, donc cela se transforme effectivement en une copie superficielle dans de tels cas avec un seul endroit libérant toujours la mémoire d'origine, par exemple (dans certains cas, en fonction de la conception). Alors qu'avec l'implication de destructeurs, les deux Fooinstances peuvent maintenant vouloir détruire le tableau dynamique, ou le vecteur, ou quelque chose à cet effet. Je trouve souvent pour certains cas particuliers qu'il est utile de pouvoir écrire certaines structures de données pour s'appuyer sur le fait que la destruction est explicite plutôt qu'implicite.
Dragon Energy

@Caleth C'est certes une chose paresseuse de ma part car si Fooil y a des pointeurs propriétaires, nous pourrions lui donner un bon copier / déplacer ctor, dtor, utiliser la sémantique des valeurs, etc., et avoir un code beaucoup plus riche et plus sûr. Mais parfois, je me retrouve à atteindre C dans les cas où je veux généraliser une structure de données ou un allocateur au niveau homogène de bits et d'octets, avec certaines hypothèses que je peux faire sur les types qui ont tendance, dans leurs cas d'utilisation étroits, à être simplifiés un peu par de telles hypothèses, je me sens un peu plus confiant à faire en C.
Dragon Energy

@Caleth Dans mon cas cependant, il y a parfois une pierre angulaire de l'architecture où je ne trouve aucune utilité à considérer les choses comme plus que des bits et des octets en mémoire pour organiser et accéder (et généralement ces cas n'impliquent rien d'autre que des POD). Ce sont les rares cas particuliers où je préfère encore C. Si vous imaginez l'allocateur de mémoire, il n'y a vraiment rien de plus à travailler que des bits et des octets, des pointeurs vides, des choses de ce genre, avec tout l'accent sur l'alignement et la mise en commun et distribuer des bits et des octets, et dans ces cas très particuliers, je trouve que C offre moins d'obstacles que C ++.
Dragon Energy

L'autre cas où j'atteins parfois C est des cas où le code pourrait en fait devenir plus généralisé et réutilisable en étant moins abstrait et en se concentrant sur les primitives, comme API void filter_image(byte* pixels, int w, int h);un exemple simple par opposition à like API void filter_image(ImageInterface& img);(ce qui couplerait notre code à une telle interface d'image, rétrécissement de son applicabilité). Dans de tels cas, je viens parfois d'implémenter de telles fonctions en C car il y a peu à gagner en C ++, et cela réduit la probabilité qu'un tel code ait besoin de modifications futures.
Dragon Energy
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.