J'ai entendu dire que la fonction static_cast
devrait être préférée au casting en style C ou simple. Est-ce vrai? Pourquoi?
La raison principale est que les casts classiques en C ne font aucune distinction entre ce que nous appelons static_cast<>()
, reinterpret_cast<>()
, const_cast<>()
et dynamic_cast<>()
. Ces quatre choses sont complètement différentes.
Une static_cast<>()
est généralement sans danger. Il existe une conversion valide dans le langage ou un constructeur approprié qui le permet. La seule fois où il y a un peu de risque, c'est lorsque vous utilisez une classe héritée. vous devez vous assurer que l'objet est bien le descendant que vous prétendez être, par des moyens externes à la langue (comme un drapeau dans l'objet). Un dynamic_cast<>()
est sécurisé tant que le résultat est vérifié (pointeur) ou qu'une exception possible est prise en compte (référence).
Une reinterpret_cast<>()
(ou une const_cast<>()
) est toujours dangereuse. Vous dites au compilateur: "croyez-moi: je sais que cela ne ressemble pas à un foo
(cela ressemble à du fait qu'il n'est pas mutable), mais c'est".
Le premier problème est qu’il est presque impossible de savoir lequel se produira dans un casting de style C sans regarder de gros morceaux de code et en disperser et connaître toutes les règles.
Supposons ces:
class CDerivedClass : public CMyBase {...};
class CMyOtherStuff {...} ;
CMyBase *pSomething; // filled somewhere
Maintenant, ces deux sont compilés de la même manière:
CDerivedClass *pMyObject;
pMyObject = static_cast<CDerivedClass*>(pSomething); // Safe; as long as we checked
pMyObject = (CDerivedClass*)(pSomething); // Same as static_cast<>
// Safe; as long as we checked
// but harder to read
Cependant, voyons ce code presque identique:
CMyOtherStuff *pOther;
pOther = static_cast<CMyOtherStuff*>(pSomething); // Compiler error: Can't convert
pOther = (CMyOtherStuff*)(pSomething); // No compiler error.
// Same as reinterpret_cast<>
// and it's wrong!!!
Comme vous pouvez le constater, il n’ya pas de moyen facile de distinguer les deux situations sans en savoir beaucoup sur toutes les classes concernées.
Le deuxième problème est que les modèles en C sont trop difficiles à localiser. Dans les expressions complexes, il peut être très difficile de voir les conversions de style C. Il est pratiquement impossible d'écrire un outil automatisé devant localiser des transtypages de style C (par exemple, un outil de recherche) sans une interface complète du compilateur C++. Par contre, il est facile de rechercher "static_cast <" ou "reinterpret_cast <".
pOther = reinterpret_cast<CMyOtherStuff*>(pSomething);
// No compiler error.
// but the presence of a reinterpret_cast<> is
// like a Siren with Red Flashing Lights in your code.
// The mere typing of it should cause you to feel VERY uncomfortable.
Cela signifie que non seulement les castes de style C sont plus dangereuses, mais qu’il est beaucoup plus difficile de toutes les trouver pour s’assurer qu’elles sont correctes.
Un conseil pragmatique: vous pouvez facilement rechercher le mot clé static_cast dans votre code source si vous envisagez de ranger le projet.
En bref :
static_cast<>()
vous donne une capacité de vérification du temps de compilation, contrairement à la distribution C-Style.static_cast<>()
peut être facilement repéré n'importe où dans un code source C++; en revanche, la distribution de C_Style est plus difficile à repérer.- Les intentions sont bien mieux transmises avec les conversions en C++.
Plus d'explications :
La distribution statique effectue des conversions entre types compatibles . Il ressemble au casting C-style, mais est plus restrictif. Par exemple, la conversion de style C permettrait à un pointeur entier de pointer vers un caractère.
char c = 10; // 1 byte int *p = (int*)&c; // 4 bytes
Comme cela entraîne un pointeur de 4 octets pointant vers un octet de mémoire allouée, l'écriture sur ce pointeur provoquera une erreur d'exécution ou remplacera une partie de la mémoire adjacente.
*p = 5; // run-time error: stack corruption
Contrairement à la conversion de style C, la conversion statique permettra au compilateur de vérifier la compatibilité des types de données pointeur et pointee, ce qui permettra au programmeur de détecter cette affectation incorrecte du pointeur lors de la compilation.
int *q = static_cast<int*>(&c); // compile-time error
En savoir plus sur:
Quelle est la différence entre le casting de styles static_cast <> et C
et
Distribution normale vs static_cast vs dynamic_cast
La question est plus vaste que d'utiliser simplement un casting statique ou un casting de style C. Il en va différemment lors de l'utilisation d'un casting de style C. Les opérateurs de transtypage C++ ont pour but de rendre ces opérations plus explicites.
Sur la surface, les conversions de style C et static_cast apparaissent de la même manière, par exemple lors du transfert d'une valeur à une autre:
int i;
double d = (double)i; //C-style cast
double d2 = static_cast<double>( i ); //C++ cast
Les deux transforment la valeur entière en double. Cependant, lorsque vous travaillez avec des pointeurs, les choses se compliquent. quelques exemples:
class A {};
class B : public A {};
A* a = new B;
B* b = (B*)a; //(1) what is this supposed to do?
char* c = (char*)new int( 5 ); //(2) that weird?
char* c1 = static_cast<char*>( new int( 5 ) ); //(3) compile time error
Dans cet exemple (1) peut-être OK parce que l'objet pointé par A est en réalité une instance de B. Mais que se passe-t-il si vous ne savez pas à ce stade du code à quoi pointe en réalité? (2) peut-être parfaitement légal (vous voulez seulement regarder un octet de l'entier), mais cela pourrait aussi être une erreur, auquel cas une erreur serait Nice, comme (3). Les opérateurs de transtypage C++ sont censés exposer ces problèmes dans le code en fournissant des erreurs de compilation ou d'exécution lorsque cela est possible.
Donc, pour un "casting de valeur" strict, vous pouvez utiliser static_cast. Si vous voulez une diffusion polymorphe au moment de l'exécution, utilisez dynamic_cast. Si vous voulez vraiment oublier les types, vous pouvez utiliser reintrepret_cast. Et pour jeter const par la fenêtre, il y a const_cast.
Ils rendent simplement le code plus explicite, de sorte qu'il semble que vous sachiez ce que vous faisiez.
static_cast
signifie que vous ne pouvez pas accidentellement const_cast
ou reinterpret_cast
, ce qui est une bonne chose.
Voir Effective C++ Introduction
C'est à peu près combien de sécurité de type vous voulez imposer.
Lorsque vous écrivez (bar) foo
(ce qui équivaut à reinterpret_cast<bar> foo
si vous n'avez pas fourni d'opérateur de conversion de type), vous dites au compilateur d'ignorer la sécurité du type et faites comme il est dit.
Lorsque vous écrivez static_cast<bar> foo
, vous demandez au compilateur de vérifier au moins que la conversion de type est logique et, pour les types intégraux, d’insérer du code de conversion.
EDIT 2014-02-26
J'ai écrit cette réponse il y a plus de 5 ans et je me suis trompé. (Voir les commentaires.) Mais il obtient toujours des votes!
static_cast, en plus de manipuler des pointeurs vers des classes, peut également être utilisé pour effectuer des conversions explicitement définies dans des classes, ainsi que pour effectuer des conversions standard entre types fondamentaux:
double d = 3.14159265;
int i = static_cast<int>(d);
Les conversions de style C sont faciles à manquer dans un bloc de code. Les conversions de style C++ ne constituent pas seulement une meilleure pratique. ils offrent beaucoup plus de flexibilité.
reinterpret_cast autorise les conversions intégrales en types de pointeur, mais peut être dangereux si utilisé à mauvais escient.
static_cast offre une bonne conversion pour les types numériques, par exemple. d'énumérations à ints ou ints en floats ou en tout type de données dont vous êtes sûr du type. Il n'effectue aucune vérification à l'exécution.
en revanche, dynamic_cast effectuera ces vérifications en signalant les affectations ou les conversions ambiguës. Il ne fonctionne que sur les pointeurs et les références et engendre une surcharge.
Il y en a quelques autres mais ce sont les principaux que vous rencontrerez.