Domanda

Scrivo codice C e C++ da quasi vent'anni, ma c'è un aspetto di questi linguaggi che non ho mai veramente capito.Ovviamente ho usato calchi normali, ad es.

MyClass *m = (MyClass *)ptr;

ovunque, ma sembra che ci siano altri due tipi di calchi e non so la differenza.Qual è la differenza tra le seguenti righe di codice?

MyClass *m = (MyClass *)ptr;
MyClass *m = static_cast<MyClass *>(ptr);
MyClass *m = dynamic_cast<MyClass *>(ptr);
È stato utile?

Soluzione

static_cast

static_cast viene utilizzato nei casi in cui si desidera sostanzialmente invertire una conversione implicita, con alcune restrizioni e aggiunte. static_cast non esegue controlli di runtime.Dovrebbe essere usato se sai che ti riferisci a un oggetto di un tipo specifico e quindi un controllo non sarebbe necessario.Esempio:

void func(void *data) {
  // Conversion from MyClass* -> void* is implicit
  MyClass *c = static_cast<MyClass*>(data);
  ...
}

int main() {
  MyClass c;
  start_thread(&func, &c)  // func(&c) will be called
      .join();
}

In questo esempio, sai di aver superato a MyClass oggetto e quindi non è necessario un controllo in fase di esecuzione per garantirlo.

Dynamic_cast

dynamic_cast è utile quando non sai qual è il tipo dinamico dell'oggetto.Restituisce un puntatore nullo se l'oggetto a cui si fa riferimento non contiene il tipo a cui è stato eseguito il cast come classe base (quando si esegue il cast su un riferimento, a bad_cast in questo caso viene lanciata un'eccezione).

if (JumpStm *j = dynamic_cast<JumpStm*>(&stm)) {
  ...
} else if (ExprStm *e = dynamic_cast<ExprStm*>(&stm)) {
  ...
}

Non puoi usare dynamic_cast se esegui il downcast (trasmetti a una classe derivata) e il tipo di argomento non è polimorfico.Ad esempio, il seguente codice non è valido, perché Base non contiene alcuna funzione virtuale:

struct Base { };
struct Derived : Base { };
int main() {
  Derived d; Base *b = &d;
  dynamic_cast<Derived*>(b); // Invalid
}

Un "up-cast" (cast alla classe base) è sempre valido con entrambi static_cast E dynamic_cast, e anche senza alcun cast, poiché un "up-cast" è una conversione implicita.

Cast regolare

Questi calchi sono anche chiamati cast in stile C.Un cast in stile C è sostanzialmente identico a provare una serie di sequenze di cast C++ e prendere il primo cast C++ che funziona, senza mai considerare dynamic_cast.Inutile dire che questo è molto più potente in quanto combina tutti questi elementi const_cast, static_cast E reinterpret_cast, ma è anche pericoloso, perché non utilizza dynamic_cast.

Inoltre, i lanci in stile C non solo ti permettono di farlo, ma ti permettono anche di lanciare in sicurezza una classe base privata, mentre l'"equivalente" static_cast sequenza ti darebbe un errore in fase di compilazione per questo.

Alcune persone preferiscono i calchi in stile C a causa della loro brevità.Li utilizzo solo per cast numerici e utilizzo i cast C++ appropriati quando sono coinvolti tipi definiti dall'utente, poiché forniscono un controllo più rigoroso.

Altri suggerimenti

Cast statico

Il cast statico esegue conversioni tra tipi compatibili.È simile al cast in stile C, ma è più restrittivo.Ad esempio, il cast in stile C consentirebbe a un puntatore intero di puntare a un carattere.

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

Poiché ciò risulta in un puntatore a 4 byte che punta a 1 byte di memoria allocata, la scrittura su questo puntatore causerà un errore di runtime o sovrascriverà parte della memoria adiacente.

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

A differenza del cast in stile C, il cast statico consentirà al compilatore di verificare che i tipi di dati puntatore e puntatore siano compatibili, il che consente al programmatore di rilevare questa assegnazione errata del puntatore durante la compilazione.

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

Reinterpretare il cast

Per forzare la conversione del puntatore, allo stesso modo del cast in stile C in background, verrebbe invece utilizzato il cast di reinterpretazione.

int *r = reinterpret_cast<int*>(&c); // forced conversion

Questo cast gestisce le conversioni tra determinati tipi non correlati, ad esempio da un tipo di puntatore a un altro tipo di puntatore incompatibile.Eseguirà semplicemente una copia binaria dei dati senza alterare il modello di bit sottostante.Si noti che il risultato di un'operazione di livello così basso è specifico del sistema e quindi non portabile.Dovrebbe essere usato con cautela se non può essere evitato del tutto.

Cast dinamico

Questo viene utilizzato solo per convertire puntatori a oggetti e riferimenti a oggetti in altri tipi di puntatori o riferimenti nella gerarchia di ereditarietà.È l'unico cast che garantisce che l'oggetto puntato possa essere convertito, eseguendo un controllo runtime che il puntatore si riferisca ad un oggetto completo del tipo di destinazione.Affinché questo controllo in fase di esecuzione sia possibile, l'oggetto deve essere polimorfico.Cioè, la classe deve definire o ereditare almeno una funzione virtuale.Questo perché il compilatore genererà solo le informazioni sul tipo di runtime necessarie per tali oggetti.

Esempi di cast dinamico

Nell'esempio seguente, un puntatore MyChild viene convertito in un puntatore MyBase utilizzando un cast dinamico.Questa conversione da derivato a base ha esito positivo, poiché l'oggetto Child include un oggetto Base completo.

class MyBase 
{ 
  public:
  virtual void test() {}
};
class MyChild : public MyBase {};



int main()
{
  MyChild *child = new MyChild();
  MyBase  *base = dynamic_cast<MyBase*>(child); // ok
}

L'esempio successivo tenta di convertire un puntatore MyBase in un puntatore MyChild.Poiché l'oggetto Base non contiene un oggetto Child completo, questa conversione del puntatore fallirà.Per indicare ciò, il cast dinamico restituisce un puntatore nullo.Ciò fornisce un modo conveniente per verificare se una conversione è riuscita o meno durante l'esecuzione.

MyBase  *base = new MyBase();
MyChild *child = dynamic_cast<MyChild*>(base);


if (child == 0) 
std::cout << "Null pointer returned";

Se viene convertito un riferimento anziché un puntatore, il cast dinamico fallirà generando un'eccezione bad_cast.Questo deve essere gestito utilizzando un'istruzione try-catch.

#include <exception>
// …  
try
{ 
  MyChild &child = dynamic_cast<MyChild&>(*base);
}
catch(std::bad_cast &e) 
{ 
  std::cout << e.what(); // bad dynamic_cast
}

Cast dinamico o statico

Il vantaggio dell'utilizzo di un cast dinamico è che consente al programmatore di verificare se una conversione è riuscita o meno durante l'esecuzione.Lo svantaggio è che si verifica un sovraccarico delle prestazioni associato all'esecuzione di questo controllo.Per questo motivo nel primo esempio sarebbe stato preferibile utilizzare un cast statico, poiché una conversione da derivata a base non fallirà mai.

MyBase *base = static_cast<MyBase*>(child); // ok

Tuttavia, nel secondo esempio la conversione potrebbe avere esito positivo o negativo.Fallirà se l'oggetto MyBase contiene un'istanza MyBase e avrà successo se contiene un'istanza MyChild.In alcune situazioni ciò potrebbe non essere noto fino al momento dell'esecuzione.In questo caso il cast dinamico è una scelta migliore rispetto al cast statico.

// Succeeds for a MyChild object
MyChild *child = dynamic_cast<MyChild*>(base);

Se la conversione da base a derivata fosse stata eseguita utilizzando un cast statico anziché dinamico, la conversione non avrebbe fallito.Avrebbe restituito un puntatore che si riferiva a un oggetto incompleto.Dereferenziare un puntatore di questo tipo può portare a errori di runtime.

// Allowed, but invalid
MyChild *child = static_cast<MyChild*>(base);

// Incomplete MyChild object dereferenced
(*child);

Cast costante

Questo viene utilizzato principalmente per aggiungere o rimuovere il modificatore const di una variabile.

const int myConst = 5;
int *nonConst = const_cast<int*>(&myConst); // removes const

Sebbene const cast consenta di modificare il valore di una costante, tale operazione costituisce comunque un codice non valido che potrebbe causare un errore di runtime.Ciò potrebbe verificarsi ad esempio se la costante si trovava in una sezione della memoria di sola lettura.

*nonConst = 10; // potential run-time error

Const cast viene invece utilizzato principalmente quando esiste una funzione che accetta come argomento un puntatore non costante, anche se non modifica il puntatore.

void print(int *p) 
{
   std::cout << *p;
}

Alla funzione può quindi essere passata una variabile costante utilizzando un cast const.

print(&myConst); // error: cannot convert 
                 // const int* to int*

print(nonConst); // allowed

Fonte e ulteriori spiegazioni

Dovresti guardare l'articolo Programmazione C++/Casting di tipi.

Contiene una buona descrizione di tutti i diversi tipi di cast.Quanto segue tratto dal link sopra:

const_cast

const_cast (espressione) il const_cast <> () viene utilizzato per aggiungere/rimuovere const (Ness) (o volatile) di una variabile.

static_cast

static_cast (espressione) lo static_cast <> () viene utilizzato per lanciare tra i tipi interi.'EG' char-> long, int-> corto ecc.

Il cast statico viene anche utilizzato per lanciare puntatori a tipi correlati, ad esempio la fusione del tipo* al tipo appropriato.

Dynamic_cast

Il cast dinamico viene utilizzato per convertire puntatori e riferimenti in fase di esecuzione, generalmente allo scopo di lanciare un puntatore o riferimento su o giù per una catena di eredità (gerarchia ereditaria).

Dynamic_cast(espressione)

Il tipo di destinazione deve essere un puntatore o un tipo di riferimento e l'espressione deve valutare su un puntatore o un riferimento.Il cast dinamico funziona solo quando il tipo di oggetto a cui si riferisce l'espressione è compatibile con il tipo di destinazione e la classe base ha almeno una funzione dei membri virtuali.In caso contrario, e il tipo di espressione che viene lanciato è un puntatore, Null viene restituito, se un cast dinamico su un riferimento non riesce, viene lanciata un'eccezione BAD_CHACT.Quando non fallisce, il cast dinamico restituisce un puntatore o un riferimento del tipo di destinazione all'oggetto a cui si riferiva l'espressione.

reinterpret_cast

Reinterpret cast esegue semplicemente il cast di un tipo bit per bit a un altro.Qualsiasi puntatore o tipo integrale può essere lanciato su qualsiasi altro con cast reinterpretato, consentendo facilmente l'abuso.Ad esempio, con reinterpretuet cast si potrebbe, a quel punto, lanciare un puntatore intero a un puntatore di stringhe.

Per tua informazione, credo che Bjarne Stroustrup abbia affermato che i cast in stile C devono essere evitati e che dovresti usare static_cast o Dynamic_cast se possibile.

Domande frequenti sullo stile C++ di Barne Stroustrup

Segui quel consiglio per quello che vuoi.Sono lungi dall'essere un guru del C++.

Evitare l'uso di gessi di tipo C.

I cast in stile C sono un mix di const e reinterpret cast ed è difficile da trovare e sostituire nel codice.Un programmatore di applicazioni C++ dovrebbe evitare il cast in stile C.

I cast in stile C uniscono const_cast, static_cast e reinterpret_cast.

Vorrei che C++ non avesse cast in stile C.I cast C++ risaltano correttamente (come dovrebbero;i cast normalmente indicano che si sta facendo qualcosa di brutto) e distinguono correttamente tra i diversi tipi di conversione eseguiti dai cast.Permettono anche di scrivere funzioni dall'aspetto simile, ad es.boost::lexical_cast, che è piuttosto carino dal punto di vista della coerenza.

dynamic_cast ha il controllo del tipo in fase di esecuzione e funziona solo con riferimenti e puntatori, mentre static_cast non offre il controllo del tipo di runtime.Per informazioni complete, vedere l'articolo MSDN Operatore static_cast.

dynamic_cast supporta solo tipi puntatore e riferimento.Ritorna NULL se il cast è impossibile se il tipo è un puntatore o genera un'eccezione se il tipo è un tipo di riferimento.Quindi, dynamic_cast può essere usato per verificare se un oggetto è di un determinato tipo, static_cast non può (ti ritroverai semplicemente con un valore non valido).

I cast in stile C (e altri) sono stati trattati nelle altre risposte.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top