web-dev-qa-db-fra.com

Pourquoi utiliser static_cast <int> (x) au lieu de (int) x?

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?

611
Tommy Herbert

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.

593
Euro Micelli

Un conseil pragmatique: vous pouvez facilement rechercher le mot clé static_cast dans votre code source si vous envisagez de ranger le projet.

109
Karl

En bref :

  1. static_cast<>() vous donne une capacité de vérification du temps de compilation, contrairement à la distribution C-Style.
  2. 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.
  3. 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

68
Breeze

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.

27
Dusty Campbell

static_cast signifie que vous ne pouvez pas accidentellement const_cast ou reinterpret_cast, ce qui est une bonne chose.

24
DrPizza
  1. Permet de retrouver facilement les conversions dans votre code en utilisant grep ou des outils similaires.
  2. Explicite le type de casting que vous faites et fait appel à l'aide du compilateur pour le faire respecter. Si vous souhaitez uniquement vous débarrasser de const-ness, vous pouvez utiliser const_cast, ce qui ne vous autorisera pas à effectuer d'autres types de conversions.
  3. Les cast sont intrinsèquement laids - vous en tant que programmeur annulez la façon dont le compilateur traiterait normalement votre code. Vous dites au compilateur: "Je sais mieux que vous." Cela étant, il est logique que l'exécution d'un casting soit une tâche moyennement pénible, et que cela apparaisse dans votre code, car ils sont une source probable de problèmes.

Voir Effective C++ Introduction

7
JohnMcG

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!

7
Pitarou

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);
4
prakash

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.

4
Konrad