Est-ce que C # vous donne «moins de corde pour vous pendre» que C ++? [fermé]

14

Joel Spolsky a qualifié le C ++ de "suffisamment de corde pour se suspendre" . En fait, il résumait "Effective C ++" par Scott Meyers:

C'est un livre qui dit essentiellement que le C ++ est assez de corde pour vous pendre, puis quelques kilomètres supplémentaires de corde, puis quelques pilules suicide qui sont déguisées en M&M ...

Je n'ai pas de copie du livre, mais il y a des indications qu'une grande partie du livre se rapporte aux pièges de la gestion de la mémoire qui semblent être rendus sans objet en C # car le runtime gère ces problèmes pour vous.

Voici mes questions:

  1. C # évite-t-il les pièges qui sont évités en C ++ uniquement par une programmation minutieuse? Si oui, dans quelle mesure et comment sont-ils évités?
  2. Y a-t-il de nouveaux pièges différents en C # dont un nouveau programmeur C # devrait être conscient? Si c'est le cas, pourquoi ne pourraient-ils pas être évités par la conception de C #?
alx9r
la source
10
De la FAQ : Your questions should be reasonably scoped. If you can imagine an entire book that answers your question, you’re asking too much.. Je crois que cela se qualifie comme une telle question ...
Oded
@Oded Faites-vous référence à la question de titre limitée aux caractères? Ou mes 3+ questions plus précises dans le corps de mon message?
alx9r
3
Franchement - à la fois le titre et chacune des «questions plus précises».
Oded
3
J'ai commencé une discussion Meta sur cette question.
Odé
1
Concernant votre 3ème question maintenant supprimée, la série Effective C # de Bill Wagner (maintenant 3 livres) m'a appris plus sur la programmation C # bien que tout ce que j'ai lu sur le sujet. L'examen par Martins de EC # a raison en ce qu'il ne peut jamais remplacer directement le C ++ efficace, mais il a tort de penser que cela devrait l'être. Une fois que vous n'avez plus à vous soucier des erreurs faciles , vous devez passer à des erreurs plus dures .
Mark Booth

Réponses:

33

La différence fondamentale entre C ++ et C # provient d' un comportement indéfini .

Cela n'a rien à voir avec la gestion manuelle de la mémoire. Dans les deux cas, c'est un problème résolu.

C / C ++:

En C ++, lorsque vous faites une erreur, le résultat n'est pas défini.
Ou, si vous essayez de faire certains types d'hypothèses sur le système (par exemple débordement d'entier signé), il est probable que votre programme ne soit pas défini.

Peut-être lire cette série en 3 parties sur le comportement indéfini.

C'est ce qui rend C ++ si rapide - le compilateur n'a pas à se soucier de ce qui se passe lorsque les choses tournent mal, il peut donc éviter de vérifier l'exactitude.

C #, Java, etc.

En C #, vous avez la garantie que de nombreuses erreurs vous exploseront au visage en tant qu'exceptions, et vous en aurez bien plus sur le système sous-jacent.
C'est une barrière fondamentale pour rendre C # aussi rapide que C ++, mais c'est aussi une barrière fondamentale pour rendre C ++ sûr, et cela rend C # plus facile à utiliser et à déboguer.

Tout le reste n'est que de la sauce.

user541686
la source
Tous les éléments non définis sont vraiment définis par l'implémentation, donc si vous débordez un entier non signé dans Visual Studio, vous obtiendrez une exception si vous avez activé les bons indicateurs du compilateur. Maintenant, je sais que c'est de cela dont vous parlez, mais ce n'est pas un comportement indéfini , c'est juste que les gens ne le vérifient généralement pas. (même avec un comportement vraiment indéfini comme operator ++, il est bien défini par chaque compilateur). Vous pourriez dire la même chose avec C #, seulement il n'y a qu'une seule implémentation - beaucoup de «comportement indéfini» si vous exécutez en mono - par exemple. bugzilla.xamarin.com/show_bug.cgi?id=310
gbjbaanb
1
Est-il vraiment défini ou défini uniquement par l'implémentation actuelle de .net sur la version actuelle de Windows? Même le comportement indéfini de c ++ est entièrement défini si vous le définissez comme tout ce que fait g ++.
Martin Beckett
6
Le débordement d'entiers non signés n'est pas du tout UB. C'est des entiers signés débordants qui sont UB.
DeadMG
6
@gbjbaanb: Comme DeadMG l'a dit - le débordement d'entier signé n'est pas défini. Ce n'est pas défini par l'implémentation. Ces phrases ont des significations spécifiques dans la norme C ++, et ce n'est pas la même chose. Ne faites pas cette erreur.
user541686
1
@CharlesSalvia: Euh, comment exactement "C ++ permet-il de profiter plus facilement du cache CPU" que C #? Et quel type de contrôle C ++ vous donne-t-il sur la mémoire que vous ne pouvez pas avoir en C #?
user541686
12

C # évite-t-il les pièges qui sont évités en C ++ uniquement par une programmation minutieuse? Si oui, dans quelle mesure et comment sont-ils évités?

La plupart le font, certains non. Et bien sûr, il en fait de nouveaux.

  1. Comportement indéfini - Le plus grand écueil avec C ++ est qu'il y a beaucoup de langage non défini. Le compilateur peut littéralement faire exploser l'univers lorsque vous faites ces choses, et ça ira. Naturellement, cela n'est pas courant , mais il est assez courant que votre programme fonctionne correctement sur une machine et pour de bonnes raisons ne fonctionne pas sur une autre. Ou pire, agissez subtilement différemment. C # a quelques cas de comportement indéfini dans ses spécifications, mais ils sont rares et dans des zones du langage qui sont peu fréquentées. C ++ a la possibilité d'exécuter un comportement indéfini chaque fois que vous faites une déclaration.

  2. Fuites de mémoire - C'est moins une préoccupation pour le C ++ moderne, mais pour les débutants et pendant environ la moitié de sa durée de vie, C ++ a rendu la fuite de mémoire super facile. Un C ++ efficace est venu autour de l'évolution des pratiques pour éliminer cette préoccupation. Cela dit, C # peut toujours fuir la mémoire. Le cas le plus courant rencontré par les utilisateurs est la capture d'événements. Si vous avez un objet et mettez l'une de ses méthodes en tant que gestionnaire d'un événement, le propriétaire de cet événement doit être GC'd pour que l'objet meure. La plupart des débutants ne réalisent pas que le gestionnaire d'événements compte comme référence. Il y a aussi des problèmes à ne pas disposer de ressources jetables qui peuvent fuir la mémoire, mais ce ne sont pas aussi communs que les pointeurs dans C ++ pré-efficace.

  3. Compilation - C ++ a un modèle de compilation retardée. Cela conduit à un certain nombre de trucs pour bien jouer avec et réduire les temps de compilation.

  4. Chaînes - Le C ++ moderne améliore un peu les choses, mais char*est responsable d'environ 95% de toutes les violations de sécurité avant l'an 2000. Pour les programmeurs expérimentés, ils se concentreront sur std::string, mais c'est toujours quelque chose à éviter et un problème dans les bibliothèques anciennes / pires . Et c'est prier pour que vous n'ayez pas besoin du support Unicode.

Et vraiment, c'est la pointe de l'iceberg. Le principal problème est que C ++ est un langage très pauvre pour les débutants. C'est assez incohérent, et beaucoup d'anciens vraiment, pièges vraiment mauvais ont été résolus en changeant les idiomes. Le problème est que les débutants doivent ensuite apprendre les idiomes de quelque chose comme Effective C ++. C # élimine un grand nombre de ces problèmes et rend le reste moins préoccupant jusqu'à ce que vous progressiez dans le parcours d'apprentissage.

Y a-t-il de nouveaux pièges différents en C # dont un nouveau programmeur C # devrait être conscient? Si oui, pourquoi ne pourraient-ils pas être évités par la conception de C #?

J'ai mentionné le problème d'événement "fuite de mémoire". Ce n'est pas un problème de langage tant que le programmeur attend quelque chose que le langage ne peut pas faire.

Un autre est que le finaliseur d'un objet C # n'est pas techniquement garanti pour être exécuté par le runtime. Ce n'est généralement pas importance, mais cela fait que certaines choses sont conçues différemment que vous ne le pensez.

Un autre demi-piège dans lequel j'ai vu des programmeurs est la sémantique de capture des fonctions anonymes. Lorsque vous capturez une variable, vous capturez la variable . Exemple:

List<Action> actions = new List<Action>();
for(int x = 0; x < 10; ++x ){
    actions.Add(() => Console.WriteLine(x));
}

foreach(var action in actions){
    action();
}

Ne fait pas ce que l'on pense naïvement. Cela imprime10 10 fois.

Je suis sûr qu'il y a un certain nombre d'autres que j'oublie, mais le principal problème est qu'ils sont moins omniprésents.

Telastyn
la source
4
Les fuites de mémoire appartiennent au passé, tout comme les autres char*. Sans oublier que vous pouvez toujours fuir la mémoire en C # très bien.
DeadMG
2
Appeler des modèles "coller des chaînes glorifiées" est un peu trop. Les modèles sont vraiment l'une des meilleures fonctionnalités de C ++.
Charles Salvia
2
@CharlesSalvia Bien sûr, ils sont la caractéristique vraiment distinctive de C ++. Et oui, c'est peut-être une simplification excessive de l'impact de la compilation. Mais ils ont un impact disproportionné sur les temps de compilation et la taille de sortie, surtout si vous ne faites pas attention.
Telastyn
2
@deadMG certainement, bien que je soutienne que la plupart des astuces de méta-programmation de modèle utilisées / nécessaires en C ++ sont ... mieux implémentées via un mécanisme différent.
Telastyn
2
@Telastyn tout l'intérêt de type_traits est d'obtenir des informations de type au moment de la compilation afin que vous puissiez utiliser ces informations pour faire des choses comme des modèles spécialisés ou des fonctions de surcharge de manière spécifique en utilisantenable_if
Charles Salvia
10

À mon avis, les dangers du C ++ sont quelque peu exagérés.

Le danger essentiel est le suivant: tandis que C # vous permet d'effectuer des opérations de pointeur «non sécurisées» à l'aide de la commande unsafe mot clé, C ++ (étant principalement un surensemble de C) vous permettra d'utiliser des pointeurs chaque fois que vous en aurez envie. Outre les dangers habituels inhérents à l'utilisation de pointeurs (qui sont les mêmes avec C), comme les fuites de mémoire, les débordements de tampon, les pointeurs pendants, etc., C ++ introduit de nouvelles façons pour vous de sérieusement bousiller les choses.

Cette "corde supplémentaire", pour ainsi dire, dont parlait Joel Spolsky , se résume essentiellement à une chose: écrire des cours qui gèrent en interne leur propre mémoire, également connue sous le nom de " Règle de 3 " (qui peut maintenant être appelée la Règle de 4 ou règle de 5 en C ++ 11). Cela signifie que si vous souhaitez écrire une classe qui gère ses propres allocations de mémoire en interne, vous devez savoir ce que vous faites, sinon votre programme se bloquera probablement. Vous devez soigneusement créer un constructeur, un constructeur de copie, un destructeur et un opérateur d'affectation, ce qui est étonnamment facile à se tromper, ce qui entraîne souvent des plantages bizarres lors de l'exécution.

TOUTEFOIS , dans la programmation C ++ quotidienne réelle, il est très rare d'écrire une classe qui gère sa propre mémoire, il est donc trompeur de dire que les programmeurs C ++ doivent toujours être "prudents" pour éviter ces pièges. Habituellement, vous ferez simplement quelque chose de plus comme:

class Foo
{
    public:

    Foo(const std::string& s) 
        : m_first_name(s)
    { }

    private:

    std::string m_first_name;
};

Cette classe ressemble assez à ce que vous feriez en Java ou en C # - elle ne nécessite aucune gestion de mémoire explicite (car la classe de bibliothèque std::strings'occupe de tout cela automatiquement), et aucune substance "Rule of 3" n'est requise du tout depuis la valeur par défaut constructeur de copie et opérateur d'affectation est très bien.

Ce n'est que lorsque vous essayez de faire quelque chose comme:

class Foo
{
    public:

    Foo(const char* s)
    { 
        std::size_t len = std::strlen(s);
        m_name = new char[len + 1];
        std::strcpy(m_name, s);
    }

    Foo(const Foo& f); // must implement proper copy constructor

    Foo& operator = (const Foo& f); // must implement proper assignment operator

    ~Foo(); // must free resource in destructor

    private:

    char* m_name;
};

Dans ce cas, il peut être difficile pour les novices de corriger l'affectation, le destructeur et le constructeur de copie. Mais pour la plupart des cas, il n'y a aucune raison de le faire. C ++ permet d'éviter très facilement la gestion manuelle de la mémoire 99% du temps en utilisant des classes de bibliothèque comme std::stringet std::vector.

Un autre problème connexe est la gestion manuelle de la mémoire d'une manière qui ne prend pas en compte la possibilité d'une exception levée. Comme:

char* s = new char[100];
some_function_which_may_throw();
/* ... */
delete[] s;

Si en some_function_which_may_throw()fait ne jette une exception, vous vous retrouvez avec une fuite de mémoire car la mémoire allouée sne sera jamais récupéré. Mais encore une fois, dans la pratique, ce n'est plus un problème pour la même raison que la «règle de 3» n'est plus vraiment un problème. Il est très rare (et généralement inutile) de gérer réellement votre propre mémoire avec des pointeurs bruts. Pour éviter le problème ci-dessus, tout ce que vous devez faire est d'utiliser un std::stringou std::vector, et le destructeur sera automatiquement invoqué lors du déroulement de la pile après la levée de l'exception.

Ainsi, un thème général ici est que de nombreuses fonctionnalités C ++ qui n'ont pas été héritées de C, telles que l'initialisation / destruction automatique, les constructeurs de copie et les exceptions, obligent un programmeur à être extrêmement prudent lors de la gestion manuelle de la mémoire en C ++. Mais encore une fois, ce n'est un problème que si vous avez l'intention de faire une gestion manuelle de la mémoire en premier lieu, ce qui n'est presque plus nécessaire lorsque vous avez des conteneurs standard et des pointeurs intelligents.

Donc, à mon avis, alors que le C ++ vous donne beaucoup de corde supplémentaire, il n'est presque jamais nécessaire de l'utiliser pour vous accrocher, et les pièges dont Joel parlait sont trivialement faciles à éviter dans le C ++ moderne.

Charles Salvia
la source
C'était la règle des trois en C ++ 03 et c'est maintenant la règle des quatre en C ++ 11.
DeadMG
1
Vous pouvez l'appeler "Règle de 5" pour le constructeur de copie, le constructeur de déplacement, l'affectation de copie, l'affectation de déplacement et le destructeur. Mais la sémantique de déplacement n'est pas toujours nécessaire uniquement pour une bonne gestion des ressources.
Charles Salvia
Vous n'avez pas besoin d'une affectation de déplacement et de copie distincte. L'idiome de copie et d'échange peut faire les deux opérateurs en un.
DeadMG
2
Cela répond à la question Does C# avoid pitfalls that are avoided in C++ only by careful programming?. La réponse est "pas vraiment, car il est trivialement facile d'éviter les pièges dont Joel parlait en C ++ moderne"
Charles Salvia
1
IMO, alors que les langages de haut niveau comme C # ou Java vous fournissent une gestion de la mémoire et d'autres choses qui sont censées vous aider , il ne fait pas toujours ce que vous supposez. Vous finissez toujours par vous occuper de la conception de votre code afin de ne laisser aucune fuite de mémoire (ce qui n'est pas exactement ce que vous appelleriez en C ++). D'après mon expérience, je trouve qu'il est encore plus facile de gérer la mémoire en C ++ parce que vous savez que des destructeurs seront appelés et dans la plupart des cas, ils nettoient. Après tout, C ++ a des pointeurs intelligents pour les cas où la conception ne permet pas une gestion efficace de la mémoire. C ++ est génial mais pas pour les nuls.
Pijusn
3

Je ne serais pas vraiment d'accord. Peut-être moins de pièges que le C ++ tel qu'il existait en 1985.

C # évite-t-il les pièges qui sont évités en C ++ uniquement par une programmation minutieuse? Si oui, dans quelle mesure et comment sont-ils évités?

Pas vraiment. Des règles comme la règle des trois ont perdu une importance considérable en C ++ 11 grâce à unique_ptret shared_ptrétant normalisées. Utiliser les classes Standard d'une manière vaguement sensée n'est pas du "codage soigneux", c'est du "codage de base". De plus, la proportion de la population C ++ qui est encore suffisamment stupide, mal informée ou les deux pour faire des choses comme la gestion manuelle de la mémoire est beaucoup plus faible qu'auparavant. La réalité est que les enseignants qui souhaitent démontrer de telles règles doivent passer des semaines à essayer de trouver des exemples où elles s'appliquent toujours, car les classes standard couvrent pratiquement tous les cas d'utilisation imaginables. De nombreuses techniques efficaces C ++ ont suivi le même chemin - le chemin du dodo. Beaucoup d'autres ne sont pas vraiment spécifiques à C ++. Laisse moi voir. En sautant le premier élément, les dix suivants sont les suivants:

  1. Ne codez pas C ++ comme c'est C. C'est vraiment du bon sens.
  2. Restreignez vos interfaces et utilisez l'encapsulation. OOP.
  3. Les rédacteurs de code d'initialisation en deux phases doivent être gravés sur le bûcher. OOP.
  4. Sachez ce qu'est la sémantique de la valeur. Est-ce vraiment spécifique au C ++?
  5. Restreignez vos interfaces, cette fois d'une manière légèrement différente. OOP.
  6. Destructeurs virtuels. Ouais. Celui-ci est probablement encore valable, quelque peu. finalet overrideont aidé à changer ce jeu en particulier pour le mieux. Faites votre destructeur overrideet vous garantissez une belle erreur de compilation si vous héritez de quelqu'un qui n'a pas fait son destructeur virtual. Faites votre classe finalet aucun mauvais gommage ne peut venir et en hériter accidentellement sans destructeur virtuel.
  7. De mauvaises choses se produisent si les fonctions de nettoyage échouent. Ce n'est pas vraiment spécifique à C ++ - vous pouvez voir les mêmes conseils pour Java et C # - et, à peu près, à peu près tous les langages. Avoir des fonctions de nettoyage qui peuvent échouer est tout simplement mauvais et il n'y a rien de C ++ ou même de POO à propos de cet élément.
  8. Soyez conscient de la façon dont l'ordre des constructeurs influence les fonctions virtuelles. De manière hilarante, en Java (actuel ou passé), il appellerait tout simplement incorrectement la fonction de la classe Derived, ce qui est encore pire que le comportement de C ++. Quoi qu'il en soit, ce problème n'est pas spécifique à C ++.
  9. Les surcharges de l'opérateur doivent se comporter comme prévu. Pas vraiment spécifique. Enfer, ce n'est même pas spécifique à la surcharge de l'opérateur, la même chose pourrait être appliquée à n'importe quelle fonction - ne lui donnez pas un nom et faites-le faire quelque chose de complètement intuitif.
  10. Ceci est actuellement considéré comme une mauvaise pratique. Tous les opérateurs d'affectation à sécurité d'exception élevée gèrent très bien l'auto-affectation, et l'auto-affectation est en fait une erreur de programme logique, et la vérification de l'auto-affectation ne vaut tout simplement pas le coût des performances.

Évidemment, je ne vais pas passer en revue tous les éléments C ++ effectifs, mais la plupart d'entre eux appliquent simplement des concepts de base au C ++. Vous trouverez les mêmes conseils dans n'importe quel langage d'opérateur surchargeable orienté objet de type valeur. Les destructeurs virtuels sont à peu près le seul à être un piège C ++ et sont toujours valides - bien que, sans doute, avec la finalclasse C ++ 11, il ne soit pas aussi valide qu'il l'était. Rappelez-vous qu'effective C ++ a été écrit lorsque l'idée d'appliquer la POO, et les fonctionnalités spécifiques de C ++, était encore très nouvelle. Ces éléments ne concernent guère les pièges de C ++ et plus sur la façon de gérer le changement de C et d'utiliser correctement la POO.

Edit: Les pièges de C ++ n'incluent pas des choses comme les pièges de malloc. Je veux dire, d'une part, chaque écueil que vous pouvez trouver dans le code C, vous pouvez également trouver dans le code C # dangereux, donc ce n'est pas particulièrement pertinent, et deuxièmement, ce n'est pas parce que la norme le définit pour l'interopération que l'utilisation est considérée comme C ++ code. La norme définit gotoégalement, mais si vous deviez écrire un tas géant de gâchis de spaghetti en l'utilisant, je considérerais que c'est votre problème, pas celui de la langue. Il y a une grande différence entre «codage soigneux» et «suivre les idiomes de base du langage».

Y a-t-il de nouveaux pièges différents en C # dont un nouveau programmeur C # devrait être conscient? Si oui, pourquoi ne pourraient-ils pas être évités par la conception de C #?

usingsuce. C'est vraiment le cas. Et je n'ai aucune idée pourquoi quelque chose de mieux n'a pas été fait. De plus, Base[] = Derived[]et à peu près toutes les utilisations d'Object, qui existent parce que les concepteurs originaux n'ont pas remarqué le succès massif que les modèles étaient en C ++, et ont décidé que "Faisons tout hériter de tout et perdons toute notre sécurité de type" était le choix le plus intelligent . Je crois aussi que vous pouvez trouver de mauvaises surprises dans des choses comme les conditions de course avec les délégués et d'autres amusements de ce genre. Ensuite, il y a d'autres choses générales, comme la façon dont les génériques sont horriblement mauvais par rapport aux modèles, le placement forcé vraiment vraiment inutile de tout dans un class, et de telles choses.

DeadMG
la source
5
Une base d'utilisateurs instruits ou de nouvelles constructions ne diminuent pas vraiment la corde. Ce ne sont que des solutions de rechange, donc moins de gens se retrouvent accrochés. Bien que ce soit un bon commentaire sur C ++ efficace et son contexte dans l'évolution du langage.
Telastyn
2
Non. Il s'agit de la façon dont un tas d'éléments dans Effective C ++ sont des concepts qui pourraient également s'appliquer à n'importe quel langage orienté objet de type valeur. Et éduquer la base d'utilisateurs pour coder le C ++ réel au lieu de C diminue définitivement la corde que C ++ vous donne. En outre, j'attendre que les nouvelles constructions de langage sont diminuaient la corde. Il s'agit de savoir comment le fait que la norme C ++ définit mallocne signifie pas que vous devez le faire, pas plus que le simple fait que vous puissiez vous prostituer gotocomme une chienne signifie que c'est une corde avec laquelle vous pouvez vous accrocher.
DeadMG
2
L'utilisation des parties C de C ++ n'est pas différente de l'écriture de tout votre code unsafeen C #, ce qui est tout aussi mauvais. Je pourrais également énumérer tous les écueils du codage C # comme C, si vous le souhaitez.
DeadMG
@DeadMG: la question devrait donc être "un programmeur C ++ a assez de corde pour se pendre tant qu'il est programmeur C"
gbjbaanb
"De plus, la proportion de la population C ++ qui est encore suffisamment stupide, mal informée ou les deux pour faire des choses comme la gestion manuelle de la mémoire est beaucoup plus faible qu'auparavant." Citation requise.
dan04
3

C # évite-t-il les pièges qui sont évités en C ++ uniquement par une programmation minutieuse? Si oui, dans quelle mesure et comment sont-ils évités?

C # a les avantages de:

  • Ne pas être rétrocompatible avec C, évitant ainsi d'avoir une longue liste de fonctionnalités de langage "diaboliques" (par exemple, des pointeurs bruts) qui sont syntaxiquement pratiques mais maintenant considérées comme de mauvais style.
  • Avoir une sémantique de référence au lieu d'une sémantique de valeur, ce qui rend au moins 10 des éléments C ++ effectifs discutable (mais introduit de nouveaux pièges).
  • Avoir un comportement moins défini par l'implémentation que C ++.
    • En particulier, en C ++ le codage de caractères de char, stringetc. est définie par l' implémentation. Le schisme entre l'approche Windows d'Unicode ( wchar_tpour UTF-16, charpour les "pages de code" obsolètes) et l'approche * nix (UTF-8) provoque de grandes difficultés dans le code multiplateforme. C #, OTOH, garantit que a stringest UTF-16.

Y a-t-il de nouveaux pièges différents en C # dont un nouveau programmeur C # devrait être conscient?

Oui: IDisposable

Existe-t-il un livre équivalent à "Effective C ++" pour C #?

Il y a un livre appelé Effective C # qui a une structure similaire à Effective C ++ .

dan04
la source
0

Non, C # (et Java) sont moins sûrs que C ++

C ++ est vérifiable localement . Je peux inspecter une seule classe en C ++ et déterminer que la classe ne perd pas de mémoire ou d'autres ressources, en supposant que toutes les classes référencées sont correctes. En Java ou C #, il est nécessaire de vérifier chaque classe référencée pour déterminer si elle nécessite une finalisation quelconque.

C ++:

{
   some_resource r(...);  // resource initialized
   ...
}  // resource destructor called, no leaks here

C #:

{
   SomeResource r = new SomeResource(...); // resource initialized
   ...
} // did I need to finalize that?  May I should have used 'using' 
  // (or in Java, a grotesque try/finally construct)?  No way to tell
  // without checking the documentation for SomeResource

C ++:

{
    auto_ptr<SomeInterface> i = SomeFactory.create(...);
    i->f(...);
} // automatic finalization and memory release.  A new implementation of
  // SomeInterface can allocate and free resources with no impact
  // on existing code

C #:

{
   SomeInterface i = SomeFactory.create(...);
   i.f(...);
   ...
} // Sure hope someone didn't create an implementation of SomeInterface
  // that requires finalization.  In C# and Java it is necessary to decide whether
  // any implementation could require finalization when the interface is defined.
  // If the initial decision is 'no finalization', then no future implementation  
  // can acquire any resource without creating potential leaks in existing code.
Kevin Cline
la source
3
... il est assez trivial dans les IDE modernes de déterminer si quelque chose hérite d'IDisposable. Le principal problème est que vous devez savoir utiliser auto_ptr(ou quelques-uns de ses proches). Telle est la corde proverbiale.
Telastyn
2
@Telastyn non, le fait est que vous utilisez toujours un pointeur intelligent, sauf si vous savez vraiment que vous n'en avez pas besoin. En C #, l'instruction using est exactement comme la corde à laquelle vous faites référence. (c'est-à-dire qu'en C ++, vous devez vous rappeler d'utiliser un pointeur intelligent, pourquoi alors C # n'est-il pas aussi mauvais même si vous devez vous rappeler de toujours utiliser une instruction using)
gbjbaanb
1
@gbjbaanb Parce que quoi? 5% dans la plupart des classes C # sont jetables? Et vous savez que vous devez les jeter s'ils sont jetables. En C ++, chaque objet est jetable. Et vous ne savez pas si votre instance particulière doit être traitée. Que se passe-t-il pour les pointeurs renvoyés qui ne proviennent pas d'une usine? Est-ce votre responsabilité de les nettoyer? Ce ne devrait pas être le cas, mais parfois c'est le cas. Et encore une fois, ce n'est pas parce que vous devez toujours utiliser un pointeur intelligent que l'option de ne pas cesser d'exister. Pour les débutants en particulier, c'est un piège important.
Telastyn
2
@Telastyn: Savoir utiliser auto_ptrest aussi simple que savoir utiliser IEnumerableou savoir utiliser des interfaces, ou ne pas utiliser de virgule flottante pour une devise ou autre. C'est une application de base de DRY. Personne qui connaît les bases de la programmation ne ferait cette erreur. Contrairement using. Le problème usingest que vous devez savoir pour chaque classe s'il est jetable (et j'espère que cela ne changera jamais), et si ce n'est pas jetable, vous interdisez automatiquement toutes les classes dérivées qui pourraient devoir être jetables.
DeadMG
2
kevin: Euh, votre réponse n'a aucun sens. Ce n'est pas la faute de C # si vous vous trompez. Vous ne dépendez PAS des finaliseurs dans un code C # correctement écrit . Si vous avez un champ qui a une Disposeméthode, vous devez l'implémenter IDisposable(de la manière «appropriée»). Si votre classe fait cela (ce qui équivaut à implémenter RAII pour votre classe en C ++), et que vous utilisez using(ce qui est comme les pointeurs intelligents en C ++), tout fonctionne parfaitement. Le finaliseur est principalement destiné à prévenir les accidents - Disposeest responsable de l'exactitude, et si vous ne l'utilisez pas, eh bien, c'est votre faute, pas C #.
user541686
0

Oui 100% oui car je pense qu'il est impossible de libérer de la mémoire et de l'utiliser en C # (en supposant qu'il soit géré et que vous ne passiez pas en mode dangereux).

Mais si vous savez programmer en C ++, ce qui n'est pas le cas d'un nombre incroyable de personnes. Tu vas bien. Comme les classes Charles Salvia ne gèrent pas vraiment leurs souvenirs car tout est géré dans les classes STL préexistantes. J'utilise rarement des pointeurs. En fait, je suis allé des projets sans utiliser un seul pointeur. (C ++ 11 facilite cela).

En ce qui concerne les fautes de frappe, les erreurs idiotes, etc. (ex: if (i=0)bc la clé s'est bloquée lorsque vous appuyez sur == très rapidement), le compilateur se plaint, ce qui est agréable car il améliore la qualité du code. D'autres exemples oublient les breakinstructions switch et ne vous permettent pas de déclarer des variables statiques dans une fonction (ce que je n'aime pas parfois mais c'est une bonne idée imo).


la source
4
Java et C # ont aggravé le problème =/ ==en utilisant ==pour l'égalité de référence et en introduisant .equalspour l'égalité de valeur. Le pauvre programmeur doit maintenant savoir si une variable est «double» ou «double» et être sûr d'appeler la bonne variante.
kevin cline
@kevincline +1 mais en C #, structvous pouvez faire ==ce qui fonctionne incroyablement bien car la plupart du temps, il n'y a que des chaînes, des entiers et des flottants (c'est-à-dire uniquement des membres struct). Dans mon propre code, je n'ai jamais ce problème, sauf lorsque je veux comparer des tableaux. Je ne pense pas avoir jamais comparé des types de liste ou non structurés (chaîne, int, float, DateTime, KeyValuePair et bien d'autres)
2
Python a bien fait les choses en utilisant ==pour l'égalité des valeurs et ispour l'égalité des références.
dan04
@ dan04 - Combien de types d'égalité pensez-vous que C # a? Voir l'excellent discours éclair d'ACCU: Certains objets sont plus égaux que d'autres
Mark Booth