65 votes

Si j'utilise des casts de style C dans mon projet C++, cela vaut-il la peine de le remanier en casts C++ ?

J'utilise des casts de style C dans mon projet C++ de 15K LOC, 90% du temps pour les casts entre les classes enfant et de base.

Même lorsque j'ai lu qu'il n'était pas bon de les utiliser et qu'elles pouvaient entraîner de graves erreurs, car elles ne sont pas aussi sûres que les casts du C++, je continue à se sentir parfaitement bien et à l'aise avec leur utilisation.

Jusqu'à présent, je n'ai pas rencontré un seul bogue dans mon projet qui ait été causé, par exemple, par une erreur de saisie accidentelle d'une distribution C-Style - vraiment.

Il y a deux raisons principales pour lesquelles je ne les ai pas utilisées :

  • Je n'en savais pas encore assez sur eux
  • Je n'ai pas aimé leur syntaxe, elle est plus verbeuse et plus difficile à lire pour moi.

Mes questions :

  • (Pourquoi) Devrais-je remanier mon projet pour utiliser des casts de type C++ ?
  • Pourquoi devrais-je utiliser les casts de style C++ pour mes futurs projets ?

J'utilise aussi bien tous les autres avantages que m'offre le C++, de la POO aux classes de base virtuelles et abstraites, en passant par les espaces de noms, la STL, et ainsi de suite, mais pas la nouvelle syntaxe du moulage des types. L'argument " Pourquoi n'utilisez-vous pas simplement le C alors ? "ne fonctionne pas pour moi.

89voto

templatetypedef Points 129554

Le principal avantage des castings de style C++ est, comme vous l'avez mentionné, la sécurité des types. Chaque caste en C++ gère un type spécifique de conversion (ou une famille de conversions liées) et le compilateur peut donc vérifier que vous ne faites pas accidentellement plus de conversions que prévu, ou une séquence de conversions qui n'est pas fondamentalement sûre.

Une chose à laquelle il faut penser est que, même si vous vous sentez à l'aise avec les casts de style C, et même si vous n'avez pas fait d'erreurs avec eux, les autres personnes travaillant sur la base de code peuvent ne pas être aussi à l'aise que vous avec ces casts. L'utilisation des opérateurs de cast rend le code plus auto-documenté. Si vous avez un cast de style C quelque part, quelqu'un d'autre lisant le code pourrait ne pas être capable de déduire immédiatement ce que vous êtes en train de faire. S'il voit quelque chose comme

T* ptr = (T*) var;

Ils pourraient ne pas être immédiatement capables de dire si c'est

  1. Un cast d'une classe de base vers une classe dérivée ou l'inverse.
  2. Un casting à dépouiller const de l'entreprise var
  3. Un cast d'un type intégral à un pointeur.

Bien qu'ils puissent probablement le déduire du contexte, il est beaucoup plus évident de comprendre ce qui se passe si vous utilisez un casting tel que

T* ptr = static_cast<T*>(var);

ou

T* ptr = const_cast<T*>(var);

Une autre raison de préférer les opérateurs de casting de style C++ est qu'ils rendent le code plus résistant aux changements. Par exemple, supposons que j'ai cette fonction :

void DoSomething(Base* ptr) {
    Derived* derived = (Derived *) ptr;
    DoSomethingElse(derived);
}

Maintenant, supposons que je me rende compte que cette fonction n'est pas censée modifier son argument, et que je décide donc de la marquer const . Par exemple :

void DoSomething(const Base* ptr) {
    Derived* derived = (Derived *) ptr;
    DoSomethingElse(derived);
}

Mais maintenant, nous avons un problème - mon plâtre de style C, qui ne faisait qu'un downcast, s'effeuille aussi maintenant. const ness. Cela peut conduire à un bug facile où DoSomethingElse modifie le pointeur que je passe, même si l'option DoSomething La méthode elle-même promet de ne pas le faire. Si à la place j'écrivais ce code comme

void DoSomething(Base* ptr) {
    Derived* derived = static_cast<Derived *>(ptr);
    DoSomethingElse(derived);
}

Et puis changez le code en ajoutant const :

void DoSomething(const Base* ptr) {
    Derived* derived = static_cast<Derived *>(ptr);
    DoSomethingElse(derived);
}

Maintenant, j'obtiendrai une erreur du compilateur me disant que mon ancien cast est cassé, ce qui peut m'amener à découvrir qu'il y a une erreur logique dans le code (à savoir, que DoSomethingElse modifie son argument, donc je ne peux pas naïvement faire de la ptr un pointeur vers const .

En résumé, l'utilisation des opérateurs de casting C++ rend le code plus lisible et plus facile à maintenir. Elle rend la logique derrière le code plus explicite. Et il rend le code moins sujet aux bogues en permettant au compilateur de détecter les erreurs soit au moment où vous les faites, soit plus tard lorsque vous revenez en arrière et modifiez l'ancien code. Je recommanderais d'essayer d'utiliser les opérateurs de casting de style C++ à l'avenir pour ces raisons principales.

Quant à savoir si vous devez revenir en arrière et essayer de remplacer vos casts actuels de style C par des casts de style C++, c'est à vous de voir. En tant qu'exercice, je vous suggère de le faire juste pour vous entraîner à apprendre les types de casts que vous utilisez. De plus, vous pourriez trouver une erreur de logique, ce qui rendrait la recherche utile !

33voto

sbi Points 100828

Dans une entreprise pour laquelle je travaillais, nous avons dû porter une application de plusieurs millions de lignes de code sur une nouvelle plate-forme. Ce qui a commencé comme "juste un autre portage vers une autre plateforme Unix" (le code fonctionnait déjà sur Windows, OSX, et une demi-douzaine de plateformes de type Unix), s'est avéré être un énorme problème. IIRC, la raison était que cette plate-forme avait plutôt exigences strictes en matière d'alignement et certains de nos lanceurs ont trébuché. exceptions matérielles en raison d'un désalignement.

Cela aurait été assez facile à corriger, si ce n'était du fait qu'un certain pourcentage de ces moules étaient Moulages de style C . Il était impossible de effectivement grep pour ceux-ci. Les recherches ont permis de trouver littéralement des dizaines de milliers de lignes de code qui devaient tous être inspectés manuellement par des êtres humains dont 99,99% étaient totalement hors sujet.
Ça n'a pas aidé que nous les humains ont tendance à manquer quelques-uns de ces moulages difficiles à repérer, qu'il faudrait alors retrouver dans une autre série de révisions des dizaines de milliers de lignes de code, dont 99,99 % sont exactement les mêmes que lors du premier tour.

Cette tâche était presque impossible à terminer dans le temps imparti, et a presque brisé le dos de l'entreprise parce qu'ils risquaient une grave pénalité contractuelle si nous dépassions la date limite.

Inutile de dire que, par la suite, il a été décidé de remplacer tous les casts de style C par leurs équivalents C++. Cependant, si cette politique avait existé avant que nous devions faire ce portage...

10voto

Crazy Eddie Points 23778

(Pourquoi) Devrais-je remanier mon projet pour utiliser des casts de type C++ ?

Oui. Pour la même raison que vous devriez les utiliser à l'avenir.

Pourquoi devrais-je utiliser les casts de style C++ pour mes futurs projets ?

La liste est en fait assez longue mais je vais passer en revue les plus importantes.

Tout d'abord, ils énoncent clairement l'intention de la distribution. En lisant "static_cast", vous savez que l'auteur avait l'intention d'effectuer un cast statique, plutôt qu'une réinterprétation ou une const.

Deuxièmement, ils font une, et seulement une chose. Vous ne pouvez pas jeter accidentellement la constance par exemple.

Ils sont faciles à rechercher. Recherchez "const_cast" pour trouver tous les casts vers/depuis const. Comment feriez-vous cela avec le style C ? Vous ne pourriez pas.

Ils ne changent pas de type de distribution lorsque la sémantique du code local change. Par exemple, (Widget*)(blah) se transformera silencieusement en un cast réinterprété si Widget cesse d'hériter du type sur lequel blah était un pointeur. Il passe également à un cast de réinterprétation si la définition de Widget n'est pas disponible localement. Si vous utilisez des cast de style nouveau à la place, vous obtiendrez un vomissement du compilateur plutôt qu'une réinterprétation silencieuse de la déclaration.

Vous pouvez faire un casting dynamique. Les castings de style C ne peuvent pas le faire.

9voto

Loki Astari Points 116129

Eh bien, deux parties :

(Pourquoi) Devrais-je remanier mon projet pour utiliser des casts de type C++ ?

Si votre code fonctionne. Alors ne le corrigez pas. Laissez-le tel qu'il est. Trouver les casts C peut être assez difficile de toute façon. Si vous travaillez sur le code et que vous tombez sur un cast C, modifiez-le si nécessaire.

Pourquoi devrais-je utiliser les casts de style C++ pour mes futurs projets ?

Parce qu'il est facile de se tromper dans les castings C.

4voto

Pete Kirkham Points 32484

Si 90% de vos lancers sont de sous-classe à classe de base, alors les supprimer serait un début - ils ne devraient pas être nécessaires. Si vous avez beaucoup de casts de base à sous-classe, alors vous faites quelque chose d'autre de mal.

Pour le reste, vous indiquez au compilateur qu'il se passe quelque chose d'étrange et que vous enfreignez les règles - soit les règles du système de types avec un reinterpret_cast ou un const_cast, vous risquez la représentation avec un static_cast vers un type intégral plus petit, ou vous enfreignez la LSP et devez connaître le type spécifique d'une sous-classe lorsqu'on vous donne une classe de base.

Rendre ces cas explicites est une bonne chose.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X