Question

J'ai entendu dire que la fonction static_cast devrait être préférée au style C ou à la diffusion simple de style fonction. Est-ce vrai? Pourquoi?

Était-ce utile?

La solution

La raison principale est que les castes classiques en C ne font aucune distinction entre ce que nous appelons static_cast < > () , réinterpréter_cast < > () , const_cast < ; > () et dynamic_cast < > () . Ces quatre choses sont complètement différentes.

Un 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ûr tant que le résultat est vérifié (pointeur) ou qu'une exception possible est prise en compte (référence).

Un reinterpret_cast < > () (ou un const_cast < > () ) est toujours dangereux. Vous dites au compilateur: "Faites-moi confiance: je sais que cela ne ressemble pas à un toto (cela ressemble à du fait qu'il n'est pas mutable), mais c'est".

Le premier problème est qu’il est presque impossible de déterminer lequel se produira dans une distribution de style C sans examiner de gros morceaux de code et en 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

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’existe 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 conversions de style C sont trop difficiles à localiser. Dans les expressions complexes, il peut être très difficile de voir les transts 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 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.

Autres conseils

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 :

     
      
  1. static_cast < > () vous permet de vérifier le temps de compilation, style C   le casting ne le fait pas.
  2.   
  3. static_cast < > () peut être repéré facilement   n'importe où dans un code source C ++; en revanche, la distribution C_Style est plus difficile à repérer.
  4.   
  5. Les intentions sont mieux communiquées avec les conversions en C ++.
  6.   
     

Plus d'explications :

     

La distribution statique effectue des conversions entre types compatibles . Il   est similaire à la distribution C-style, mais est plus restrictif. Par exemple,   la conversion de style C permettrait à un pointeur entier de pointer sur un caractère.

char c = 10;       // 1 byte
int *p = (int*)&c; // 4 bytes
     

Puisqu'il en résulte un pointeur de 4 octets pointant vers 1 octet d'alloué   mémoire, l’écriture sur ce pointeur provoquera une erreur d’exécution ou   va écraser une partie de la mémoire adjacente.

*p = 5; // run-time error: stack corruption
     

Contrairement à la distribution de style C, la distribution statique permettra au   compilateur pour vérifier que les types de données pointeur et pointee sont   compatible, ce qui permet au programmeur d’attraper cette erreur   affectation de pointeur lors de la compilation.

int *q = static_cast<int*>(&c); // compile-time error

Lisez la suite sur:
Quelle est la différence entre static_cast < > et coulée de style C
et

La question ne se résume pas à l’utilisation d’un casting statique de type static_cast ou C, car différentes choses se produisent lors de l’utilisation de conversions de style C. Les opérateurs de diffusion C ++ ont pour but de rendre ces opérations plus explicites.

À la surface, les conversions de styles statique et C ressemblent à la même chose, 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

Ces deux convertissent 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 que parce que l'objet pointé par A est en réalité une instance de B. Mais 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 bien, 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 faites.

static_cast signifie que vous ne pouvez pas accidentellement const_cast ou réinterpréter_cast , ce qui est une bonne chose.

  1. Permet de retrouver facilement les moulages dans votre code en utilisant grep ou similaire outils.
  2. Explicite quel type     de la distribution que vous faites, et engageant     l'aide du compilateur pour le faire respecter.     Si vous voulez seulement jeter     const-ness, alors vous pouvez utiliser     const_cast, ce qui ne vous permettra pas     faire d'autres types de conversions.
  3. Les moulages sont intrinsèquement laids - vous comme         un programmeur ignore comment le         compilateur traiterait habituellement votre         code. Vous dites au         compilateur, "Je sais mieux que vous."         Cela étant, c'est logique         que l'exécution d'un casting devrait être un         chose modérément douloureuse à faire, et         qu'ils doivent rester dans votre         code, car ils sont une source probable         des problèmes.

Voir C ++ effectif . Introduction

C'est à peu près combien de sécurité par 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 indiquez au compilateur d'ignorer la sécurité de type et de faire comme il est dit.

Lorsque vous écrivez static_cast < bar > toto 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 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 types fondamentaux:

double d = 3.14159265;
int    i = static_cast<int>(d);

Les conversions de style C sont faciles à rater dans un bloc de code. Les conversions de style C ++ ne constituent pas seulement une meilleure pratique. ils offrent un degré de flexibilité beaucoup plus élevé.

reinterpret_cast autorise les conversions de type 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.

dynamic_cast, d’autre part, 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 entraîne une surcharge.

Il y en a quelques autres mais ce sont les principaux que vous rencontrerez.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top