J'ai entendu dire que la static_cast
fonction devrait être préférée à la coulée de style C ou de style simple. Est-ce vrai? Pourquoi?
J'ai entendu dire que la static_cast
fonction devrait être préférée à la coulée de style C ou de style simple. Est-ce vrai? Pourquoi?
Réponses:
La principale raison est que des moulages C classiques 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.
A static_cast<>()
est généralement sûr. Il existe une conversion valide dans le langage, ou un constructeur approprié qui le rend possible. La seule fois où c'est un peu risqué, c'est lorsque vous effectuez une conversion vers une classe héritée; vous devez vous assurer que l'objet est bien le descendant que vous prétendez qu'il est, par des moyens externes au langage (comme un drapeau dans l'objet). A dynamic_cast<>()
est sûr tant que le résultat est vérifié (pointeur) ou qu'une éventuelle exception est prise en compte (référence).
A reinterpret_cast<>()
(ou a const_cast<>()
) en revanche est toujours dangereux. Vous dites au compilateur: "croyez-moi: je sais que cela ne ressemble pas à un foo
(cela semble ne pas être modifiable), mais il l'est".
Le premier problème est qu'il est presque impossible de dire lequel se produira dans une distribution de style C sans regarder de gros morceaux dispersés et connaître toutes les règles.
Supposons ceci:
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
Voyons cependant 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 voir, il n'y a pas de moyen facile de distinguer les deux situations sans en savoir beaucoup sur toutes les classes impliquées.
Le deuxième problème est que les modèles de style C sont trop difficiles à localiser. Dans les expressions complexes, il peut être très difficile de voir les modèles de style C. Il est pratiquement impossible d'écrire un outil automatisé qui doit localiser des transtypages de style C (par exemple un outil de recherche) sans un frontal complet du compilateur C ++. D'un autre côté, 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 lancers de style C sont plus dangereux, mais qu'il est beaucoup plus difficile de les trouver tous pour s'assurer qu'ils sont corrects.
static_cast
pour abattre une hiérarchie d'héritage, mais plutôt dynamic_cast
. Cela renverra soit le pointeur nul, soit un pointeur valide.
static_cast
dans cette situation. dynamic_cast
peut être plus sûr, mais ce n'est pas toujours la meilleure option. Parfois, vous savez qu'un pointeur pointe vers un sous-type donné, au moyen d'un opaque pour le compilateur, et a static_cast
est plus rapide. Dans au moins certains environnements, dynamic_cast
nécessite la prise en charge facultative du compilateur et le coût d'exécution (activation de RTTI), et vous ne voudrez peut-être pas l'activer uniquement pour quelques vérifications que vous pouvez effectuer vous-même. Le RTTI de C ++ n'est qu'une solution possible au problème.
static_cast
. L'équivalent C de reinterpret_cast
est *(destination_type *)&
, c'est-à-dire prendre l'adresse de l'objet, transtyper cette adresse en un pointeur vers un type différent, puis déréférencer. Sauf dans le cas des types de caractères ou de certains types de structures pour lesquels C définit le comportement de cette construction, il en résulte généralement un comportement indéfini en C.
int
(et int
seul), pourquoi l'utilisation static_cast<int>
vs (int)
comme le seul avantage semble être avec les variables de classe et les pointeurs. Demandez que vous développiez cela.
int
dynamic_cast
ne s'applique pas, mais toutes les autres raisons sont valables. Par exemple: supposons v
qu'un paramètre de fonction soit déclaré comme float
, alors (int)v
est static_cast<int>(v)
. Mais si vous modifiez le paramètre sur float*
, (int)v
devient silencieusement reinterpret_cast<int>(v)
while static_cast<int>(v)
est illégal et correctement capturé par le compilateur.
Une astuce pragmatique: vous pouvez rechercher facilement le mot-clé static_cast dans votre code source si vous prévoyez de ranger le projet.
int
paramètre.
En bref :
static_cast<>()
vous donne une capacité de vérification du temps de compilation, contrairement à la distribution de style C.static_cast<>()
peut être repéré facilement n'importe où dans un code source C ++; en revanche, le casting C_Style est plus difficile à repérer.- Les intentions sont exprimées beaucoup mieux à l'aide de modèles C ++.
Plus d'explication :
La distribution statique effectue des conversions entre les types compatibles . Il est similaire à la distribution de style C, 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
Étant donné que cela se traduit par un pointeur de 4 octets pointant sur 1 octet de mémoire allouée, l'écriture sur ce pointeur provoquera une erreur d'exécution ou écrasera une partie de la mémoire adjacente.
*p = 5; // run-time error: stack corruption
Contrairement à la distribution de style C, la conversion statique permettra au compilateur de vérifier que les types de données de pointeur et de pointe sont compatibles, ce qui permet au programmeur de détecter cette affectation de pointeur incorrecte lors de la compilation.
int *q = static_cast<int*>(&c); // compile-time error
En savoir plus sur:
Quelle est la différence entre static_cast <> et le casting de style C
et
Cast classique vs static_cast vs dynamic_cast
static_cast<>()
dire que c'est plus lisible. Je veux dire, parfois c'est le cas, mais la plupart du temps - en particulier sur les types entiers de base - c'est juste horriblement et inutilement verbeux. Par exemple: il s'agit d'une fonction qui permute les octets d'un mot 32 bits. Il serait presque impossible de lire à l'aide de static_cast<uint##>()
modèles, mais il est assez facile à comprendre à l'aide de (uint##)
modèles. Image du code: imgur.com/NoHbGve
always
non plus. (mais la plupart du temps oui) Dans certains cas, la distribution de style c est beaucoup plus lisible. C'est l'une des raisons pour lesquelles le casting de style c est toujours en direct et donne un coup de pied dans c ++ à mon humble avis. :) Au fait, c'était un très bel exemple
(uint32_t)(uint8_t)
) pour obtenir que les octets en plus du plus petit soient réinitialisés. Pour cela, il y a au niveau du bit et ( 0xFF &
). L'utilisation de plâtres obscurcit l'intention.
La question est plus grande que la simple utilisation de with_ static_cast ou de cast de style C car il y a différentes choses qui se produisent lors de l'utilisation de castings de style C. Les opérateurs de transtypage C ++ sont destinés à rendre ces opérations plus explicites.
Sur la surface, les transitions de style static_cast et C semblent avoir la même chose, par exemple lors de la conversion d'une valeur en une autre:
int i;
double d = (double)i; //C-style cast
double d2 = static_cast<double>( i ); //C++ cast
Les deux convertissent la valeur entière en double. Cependant, lorsque vous travaillez avec des pointeurs, les choses deviennent plus compliquées. 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 vraiment une instance de B. Mais que se passe-t-il si vous ne savez pas à ce stade du code à quoi pointe réellement un? (2) peut-être parfaitement légal (vous ne voulez regarder qu'un octet de l'entier), mais cela pourrait aussi être une erreur, auquel cas une erreur serait bien, comme (3). Les opérateurs de transtypage C ++ sont destinés à exposer ces problèmes dans le code en fournissant des erreurs de compilation ou d'exécution lorsque cela est possible.
Ainsi, pour un "casting de valeur" strict, vous pouvez utiliser static_cast. Si vous voulez une conversion polymorphe au moment de l'exécution des pointeurs, utilisez dynamic_cast. Si vous voulez vraiment oublier les types, vous pouvez utiliser reintrepret_cast. Et pour simplement jeter const par la fenêtre, il y a const_cast.
Ils rendent simplement le code plus explicite afin 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 Introduction à C ++ efficace
Il s'agit de la quantité de sécurité que vous souhaitez 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é de type et faites simplement ce qui 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 commentaires.) Mais il obtient toujours des votes positifs!
static_cast<bar>(foo)
entre parenthèses. Pareil pour reinterpret_cast<bar>(foo)
.
Les transtypages de style C sont faciles à manquer dans un bloc de code. Les conversions de style C ++ ne sont pas seulement une meilleure pratique; ils offrent une plus grande flexibilité.
reinterpret_cast permet d'intégrer des conversions de type pointeur, mais peut être dangereux s'il est mal utilisé.
static_cast offre une bonne conversion pour les types numériques, par exemple des enums en ints ou des ints en floats ou tout type de données dont vous êtes sûr de taper. Il n'effectue aucune vérification de l'exécution.
dynamic_cast, d'autre part, effectuera ces vérifications en signalant toute affectation ou conversion ambiguë. Il ne fonctionne que sur les pointeurs et les références et entraîne des frais généraux.
Il y en a quelques autres, mais ce sont les principaux que vous rencontrerez.
static_cast, outre la manipulation de 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 des types fondamentaux:
double d = 3.14159265;
int i = static_cast<int>(d);
static_cast<int>(d)
, cependant, quand (int)d
est-ce tellement plus concis et lisible? (Je veux dire dans le cas des types de base, pas des pointeurs d'objet.)
(int)d
quand int{d}
c'est tellement plus lisible? Constructeur, ou semblable à une fonction si vous en avez ()
, la syntaxe n'est pas aussi rapide à se transformer en un labyrinthe cauchemardesque de parenthèses dans des expressions complexes. Dans ce cas, ce serait int i{d}
au lieu de int i = (int)d
. Beaucoup mieux IMO. Cela dit, quand j'ai juste besoin d'un temporaire dans une expression, j'utilise static_cast
et n'ai jamais utilisé de transtypage constructeur, je ne pense pas. Je n'utilise que (C)casts
lors de l'écriture précipitée de debug cout
s ...