Question Quand static_cast, dynamic_cast, const_cast et reinterpret_cast devraient-ils être utilisés?


Quelles sont les utilisations appropriées de:

  • static_cast
  • dynamic_cast
  • const_cast
  • reinterpret_cast
  • Casting style C (type)value
  • Coulée de style de fonction type(value)

Comment décide-t-on lequel utiliser dans quels cas spécifiques?


2048
2017-12-01 20:11


origine


Réponses:


static_cast est le premier casting que vous devriez essayer d'utiliser. Il fait des choses comme des conversions implicites entre types (tels que int à floatou un pointeur vers void*), et il peut aussi appeler des fonctions de conversion explicites (ou implicites). Dans de nombreux cas, indiquant explicitement static_cast n'est pas nécessaire, mais il est important de noter que T(something) la syntaxe est équivalente à (T)something et devrait être évité (plus sur cela plus tard). UNE T(something, something_else) est sûr, cependant, et garanti d'appeler le constructeur.

static_cast peut également passer à travers les hiérarchies d'héritage. Il est inutile lors de la projection vers le haut (vers une classe de base), mais lors de la coulée vers le bas, il peut être utilisé tant qu'il ne passe pas à travers virtual héritage. Il ne fait pas de vérification, cependant, et il est un comportement indéfini à static_cast bas d'une hiérarchie à un type qui n'est pas réellement le type de l'objet.


const_cast peut être utilisé pour supprimer ou ajouter const à une variable; aucun autre casting C ++ est capable de l'enlever (même pas reinterpret_cast). Il est important de noter que modifier une ancienne const La valeur n'est indéfinie que si la variable d'origine est const; si vous l'utilisez pour prendre le const une référence à quelque chose qui n'a pas été déclaré avec const, c'est sûr. Cela peut être utile lorsque vous surchargez des fonctions de membre en fonction de const, par exemple. Il peut également être utilisé pour ajouter const à un objet, par exemple pour appeler une surcharge de fonction membre.

const_cast fonctionne également de manière similaire sur volatile, bien que ce soit moins commun.


dynamic_cast est presque exclusivement utilisé pour gérer le polymorphisme. Vous pouvez lancer un pointeur ou une référence à n'importe quel type polymorphe vers un autre type de classe (un type polymorphe a au moins une fonction virtuelle, déclarée ou héritée). Vous pouvez l'utiliser pour plus que simplement lancer vers le bas - vous pouvez lancer sur le côté ou même jusqu'à une autre chaîne. le dynamic_cast cherchera l'objet désiré et le retournera si possible. Si ça ne peut pas, ça va revenir nullptr dans le cas d'un pointeur, ou jeter std::bad_cast dans le cas d'une référence.

dynamic_cast a quelques limites, cependant. Cela ne fonctionne pas s'il y a plusieurs objets du même type dans la hiérarchie d'héritage (le soi-disant «diamant redouté») et que vous n'utilisez pas virtual héritage. Il ne peut également passer par l'héritage public - il ne parviendra jamais à traverser protected ou private héritage. Ceci est rarement un problème, cependant, car de telles formes d'héritage sont rares.


reinterpret_cast est le casting le plus dangereux, et devrait être utilisé avec parcimonie. Il transforme directement un type en un autre type, par exemple en transposant la valeur d'un pointeur à un autre ou en stockant un pointeur dans un autre. intou toutes sortes d'autres choses désagréables. En grande partie, la seule garantie que vous obtenez avec reinterpret_cast est-ce normalement si vous retournez le résultat au type original, vous obtiendrez exactement la même valeur (mais ne pas si le type intermédiaire est plus petit que le type d'origine). Il y a un certain nombre de conversions reinterpret_cast ne peux pas faire aussi. Il est principalement utilisé pour des conversions particulièrement étranges et des manipulations de bits, comme transformer un flux de données brutes en données réelles, ou stocker des données dans les bits faibles d'un pointeur aligné.


Casting style C et casting de style de fonction sont des moules en utilisant (type)object ou type(object), respectivement. Un casting de style C est défini comme le premier des éléments suivants qui réussit:

  • const_cast
  • static_cast (bien qu'ignorer les restrictions d'accès)
  • static_cast (voir ci-dessus), puis const_cast
  • reinterpret_cast
  • reinterpret_cast, puis const_cast

Il peut donc être utilisé comme un remplacement pour d'autres castes dans certains cas, mais peut être extrêmement dangereux en raison de la possibilité de dévoluer dans un reinterpret_cast, et ce dernier devrait être préféré quand la coulée explicite est nécessaire, sauf si vous êtes sûr static_cast va réussir ou reinterpret_cast va échouer. Même alors, pensez à l'option plus longue et plus explicite.

Les conversions en style C ignorent également le contrôle d'accès lors de l'exécution d'un static_cast, ce qui signifie qu'ils ont la capacité d'effectuer une opération qu'aucun autre casting ne peut. Ceci est surtout un kludge, et dans mon esprit est juste une autre raison d'éviter les jets de style C.


2207
2017-12-01 20:22



Utilisation dynamic_cast pour convertir des pointeurs / références dans une hiérarchie d'héritage.

Utilisation static_cast pour les conversions de type ordinaire.

Utilisation reinterpret_cast pour la réinterprétation de bas niveau des configurations de bits. Utilisez avec une extrême prudence.

Utilisation const_cast pour avoir jeté const/volatile. Évitez ceci à moins que vous soyez bloqué en utilisant une API const-incorrect.


283
2018-01-21 04:53



(Beaucoup d'explications théoriques et conceptuelles ont été données ci-dessus) 

Voici quelques-uns des exemples pratiques quand j'ai utilisé static_cast, dynamic_cast, const_cast, réinterpréter_cast.

(Se réfère également à ceci pour comprendre l'explication: http://www.cplusplus.com/doc/tutorial/typecasting/)

static_cast:

OnEventData(void* pData)

{
  ......

  //  pData is a void* pData, 

  //  EventData is a structure e.g. 
  //  typedef struct _EventData {
  //  std::string id;
  //  std:: string remote_id;
  //  } EventData;

  // On Some Situation a void pointer *pData
  // has been static_casted as 
  // EventData* pointer 

  EventData *evtdata = static_cast<EventData*>(pData);
  .....
}

dynamic_cast:

void DebugLog::OnMessage(Message *msg)
{
    static DebugMsgData *debug;
    static XYZMsgData *xyz;

    if(debug = dynamic_cast<DebugMsgData*>(msg->pdata)){
        // debug message
    }
    else if(xyz = dynamic_cast<XYZMsgData*>(msg->pdata)){
        // xyz message
    }
    else/* if( ... )*/{
        // ...
    }
}

const_cast:

// *Passwd declared as a const

const unsigned char *Passwd


// on some situation it require to remove its constness

const_cast<unsigned char*>(Passwd)

reinterpret_cast:

typedef unsigned short uint16;

// Read Bytes returns that 2 bytes got read. 

bool ByteBuffer::ReadUInt16(uint16& val) {
  return ReadBytes(reinterpret_cast<char*>(&val), 2);
}

151
2017-12-11 02:05



Cela pourrait aider si vous connaissez un peu les internes ...

static_cast

  • Le compilateur C ++ sait déjà comment convertir les types de scaler tels que float en int. Utilisez static_cast pour eux.
  • En général, lors de la conversion du type A en B, static_cast appelle le constructeur de B en le passant A. Si B n'a pas ce constructeur, alors vous obtenez une erreur de compilation.
  • Fonte de A* à B* réussit toujours si A et B sont en hiérarchie d'héritage (ou void) sinon vous obtenez une erreur de compilation.
  • Je t'ai eu: Si vous jetez le pointeur de base au pointeur dérivé mais si l'objet réel est le type dérivé, alors vous ne pas obtenir une erreur. Vous obtenez un mauvais pointeur et dès que vous essayez d'accéder aux membres du pointeur dérivé, vous obtenez segfault au moment de l'exécution.
  • De même pour A& à B&.
  • Je t'ai eu: Cast from Derived to Base ou viceversa crée une nouvelle copie! Pour les personnes venant de C # / Java, beaucoup d'en haut peuvent être une énorme surprise.

dynamic_cast

  • dynamic_cast utilise des informations sur le type d'exécution pour déterminer si la conversion est valide. Par exemple, (Base*) à (Derived*)peut échouer si le pointeur n'est pas réellement de type dérivé.
  • Cela signifie que dynamic_cast est très cher comparé à static_cast!
  • Pour A* à B*, si cast n'est pas valide alors dynamic_cast retournera nullptr.
  • Pour A& à B& si cast n'est pas valide, dynamic_cast lancera une exception bad_cast.
  • Contrairement aux autres conversions, il y a des frais généraux d'exécution.

const_cast

  • Alors que static_cast peut faire non const à const, il ne peut pas faire autrement. Le const_cast peut faire les deux manières.
  • Un exemple où cela est utile est l'itérer à travers un conteneur comme set<T> qui ne renvoie que ses éléments comme const pour s'assurer que vous ne changez pas sa clé. Cependant, si votre intention est de modifier les membres non clés de l'objet, alors cela devrait être correct. Vous pouvez utiliser const_cast pour supprimer la constness.
  • Un autre exemple est quand vous voulez mettre en œuvre T& foo() aussi bien que const T& foo(). Pour éviter la duplication de code, vous pouvez appliquer const_cast pour renvoyer la valeur d'une fonction d'une autre.

réinterpréter_cast

  • Cela dit essentiellement que prendre ces octets à cet emplacement de mémoire et de le considérer comme un objet donné.
  • Par exemple, vous pouvez charger 4 octets de float à 4 octets de int pour voir à quoi ressemblent les bits dans float.
  • Évidemment, si les données ne sont pas correctes pour le type, vous risquez d'obtenir une erreur de segmentation.
  • Il n'y a pas de temps d'exécution pour cette distribution.

51
2017-12-01 20:20



Est-ce que ce réponds à ta question?

Je n'ai jamais utilisé reinterpret_castEt je me demande si courir dans une caisse qui en a besoin n'est pas une odeur de mauvais design. Dans la base de code, je travaille sur dynamic_cast est beaucoup utilisé. La différence avec static_cast est-ce un dynamic_cast Est-ce que le contrôle de l'exécution peut (plus sûr) ou non (plus de frais généraux) être ce que vous voulez (voir msdn).


11
2018-05-31 14:16



En plus des autres réponses jusqu'à présent, voici un exemple non static_cast n'est pas suffisant pour que reinterpret_cast est nécessaire. Supposons qu'il existe une fonction qui dans un paramètre de sortie renvoie des pointeurs vers des objets de classes différentes (qui ne partagent pas une classe de base commune). Un véritable exemple d'une telle fonction est CoCreateInstance() (voir le dernier paramètre, qui est en fait void**). Supposons que vous demandiez une classe d'objet particulière à partir de cette fonction, de sorte que vous connaissiez à l'avance le type du pointeur (ce que vous faites souvent pour les objets COM). Dans ce cas, vous ne pouvez pas lancer de pointeur vers votre pointeur void** avec static_cast: vous avez besoin reinterpret_cast<void**>(&yourPointer).

Dans du code:

#include <windows.h>
#include <netfw.h>
.....
INetFwPolicy2* pNetFwPolicy2 = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr,
    CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),
    //static_cast<void**>(&pNetFwPolicy2) would give a compile error
    reinterpret_cast<void**>(&pNetFwPolicy2) );

cependant, static_cast fonctionne pour des pointeurs simples (pas de pointeurs vers des pointeurs), donc le code ci-dessus peut être réécrit pour éviter reinterpret_cast (au prix d'une variable supplémentaire) de la façon suivante:

#include <windows.h>
#include <netfw.h>
.....
INetFwPolicy2* pNetFwPolicy2 = nullptr;
void* tmp = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr,
    CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),
    &tmp );
pNetFwPolicy2 = static_cast<INetFwPolicy2*>(tmp);

9