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
- Un cast d'une classe de base vers une classe dérivée ou l'inverse.
- Un casting à dépouiller
const
de l'entreprise var
- 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 !