Condividi tramite


Conversioni di tipi e l'indipendenza dai tipi (C++ moderno)

In questo documento vengono identificati i problemi comuni di conversione di tipi e viene descritto come evitarli nel codice C++.

Quando si scrive un programma c++, è importante assicurare che è indipendente dai tipi.Ciò significa che ogni variabile, argomento della funzione e valore restituito di una funzione non memorizza un tipo accettabile di dati e le operazioni che includono i valori di diversi tipi "hanno significato" e non comportano perdita di dati, l'errata interpretazione di schemi di bit, o il danneggiamento della memoria.Un programma che mai in modo esplicito o implicito converte valori da un tipo a un altro è indipendente dai tipi per definizione.Tuttavia, le conversioni di tipo, anche conversioni pericolose, talvolta necessarie.Ad esempio, potrebbe essere necessario archiviare il risultato di un'operazione a virgola mobile in una variabile di tipo int, oppure potrebbe essere necessario passare il valore in int senza segno a una funzione che accetta intcon segno.Entrambi gli esempi vengono illustrate le conversioni di tipo unsafe perché possono causare perdite di dati o la nuova interpretazione di valore.

Quando il compilatore rileva una conversione non sicura, genera un errore o un avviso.Un errore impedisce la compilazione; un avviso che consente la compilazione prosegue ma indica un possibile errore nel codice.Tuttavia, anche se il programma viene compilato senza avvisi, tuttavia può contenere codice che determinerà le conversioni implicite di tipi che forniscono risultati errati.Gli errori di tipo possono essere dovuti anche alle conversioni esplicite, o dai cast, nel codice.

Tipi di conversioni implicite

Quando un'espressione contiene gli operandi di tipi incorporati diversi e non esistono cast espliciti presente, il compilatore utilizza le conversioni standard incorporate per convertire uno degli operandi in modo che la corrispondenza dei tipi.Il compilatore tenta le conversioni in una sequenza ben definita finché una non riesca.Se la conversione è selezionata una promozione, il compilatore non genera un avviso.Se la conversione è restringersi, il compilatore genera un avviso sulla perdita di dati possibile.Se l'effettiva perdita di dati si verifica dipende dai valori effettivi in questione, ma è consigliabile considerate l'avviso come un errore.Se un tipo definito dall'utente è implicito, il compilatore tenta di utilizzare le conversioni specificato nella definizione della classe.Se non viene trovata una conversione accettabile, il compilatore genera un errore e non verrà compilato il programma.Per ulteriori informazioni sulle regole che controllano le conversioni standard, vedere Conversioni standard.Per ulteriori informazioni sulle conversioni definite dall'utente, Conversioni definite dall'utente (C++/CLI)vedere.

Hh279667.collapse_all(it-it,VS.110).gifConversione di ampliamento (promozione)

In una conversione verso un tipo di dati più grande, un valore in una più piccola variabile viene assegnato a una maggiore variabile senza perdita di dati.Poiché le conversioni di ampliamento vengono sempre sicuro, il compilatore li esegue automaticamente e non genera gli avvisi.Le conversioni sono conversioni di ampliamento.

Da

Per

Il tipo integrale con segno o senza segno qualsiasi esclude long long o __int64

double

bool o char

Qualsiasi altro tipo incorporato

short o wchar_t

int, long, long long

int, long

long long

float

double

Hh279667.collapse_all(it-it,VS.110).gifConversione di restrizione (coercizione)

Il compilatore esegue conversioni di restrizione in modo implicito, ma segnala su perdita di dati.Prendere molto seriamente questi avvisi.Se si è certi che alcuna perdita di dati si verifica perché i valori in più grande variabile andranno sempre alla più piccola variabile, quindi aggiungere un cast esplicito in modo che il compilatore non più pubblicare un avviso.Se non si è certi che la conversione è sicura, aggiungere al codice un tipo di controllo runtime gestire la perdita di dati possibile in modo che non determini il programma a produrre risultati non corretti.Per suggerimenti su come gestire questo scenario, vedere Procedura: Conversione di restrizione di handle (C++).

Una conversione da un tipo a virgola mobile a un tipo integrale è una conversione di restrizione perché la parte decimale del valore in virgola mobile è stata rimossa e persa.

Il seguente esempio di codice vengono illustrate alcune conversioni verso un tipo di dati più piccolo implicite e avvisi del compilatore pubblica corrispondente.

   int i = INT_MAX + 1; //warning C4307:'+':integral constant overflow
   wchar_t wch = 'A'; //OK
   char c = wch; // warning C4244:'initializing':conversion from 'wchar_t'
                 // to 'char', possible loss of data
   unsigned char c2 = 0xfffe; //warning C4305:'initializing':truncation from
                              // 'int' to 'unsigned char'
   int j = 1.9f; // warning C4244:'initializing':conversion from 'float' to
                 // 'int', possible loss of data
   int k = 7.7; // warning C4244:'initializing':conversion from 'double' to
                // 'int', possible loss of data

Hh279667.collapse_all(it-it,VS.110).gifSigned e unsigned conversioni

Un tipo integrale con segno e la relativa controparte senza segno sono sempre la stessa dimensione, ma differiscono nello schema di bit viene interpretato come trasformazione di valore.Nell'esempio di codice seguente viene mostrato quello che si verifica quando lo stesso schema di bit viene interpretata come un valore con segno e come valore senza segno.Lo schema di bit non archiviare mai in entrambe le modifiche di num2 e di num da ciò che viene illustrato nella figura precedente.

   using namespace std;
   unsigned short num = numeric_limits<unsigned short>::max(); // #include <limits>
   short num2 = num;
   cout << "unsigned val = " << num << " signed val = " << num2 << endl;
   // Prints: unsigned val = 65535 signed val = -1

   // Go the other way.
   num2 = -1;
   num = num2;
   cout << "unsigned val = " << num << " signed val = " << num2 << endl;
   // Prints: unsigned val = 65535 signed val = -1

Si noti che i valori vengono interpretati nuovamente in entrambe le direzioni.Se il programma produce risultati dispari in cui il segno del valore risulta invertito dal previsto, cercare le conversioni implicite tra i tipi integrali con segno e unsigned.Nell'esempio seguente, il risultato dell'espressione (0 – 1) viene convertito in modo implicito da int a unsigned int quando ha archiviato in num.In questo modo lo schema di bit a essere interpretata nuovamente.

   unsigned int u3 = 0 - 1;
   cout << u3 << endl; // prints 4294967295

Il compilatore non avvisa le conversioni implicite tra i tipi integrali con segno e unsigned.Di conseguenza, è consigliabile evitare le conversioni con segno all'intero senza segno.Se non è possibile evitarli, aggiungerle al codice un controllo runtime da rilevare se il valore convertito è maggiore o uguale a zero e minore o uguale al valore massimo del tipo con segno.I valori in questo intervallo causa da con segno senza segno o da senza segno con segno senza essere interpretata nuovamente.

Hh279667.collapse_all(it-it,VS.110).gifConversioni di tipo puntatore

In molte espressioni, la matrice di tipo c in modo implicito viene convertita in un puntatore al primo elemento della matrice e le conversioni costanti possono verificarsi automaticamente.Sebbene sia utile, è potenzialmente soggetto a errori.Ad esempio, il seguente esempio di codice in modo inadeguato è assurdo, ma verrà compilato in Visual C++ e produce un risultato di "p".Innanzitutto, il valore letterale di costante di stringa "help" viene convertito in char* che indica il primo elemento della matrice, il puntatore viene incrementato da tre elementi in modo che punti all'ora ultimo elemento "p".

char* s = "Help" + 3;

Conversioni esplicite (cast)

Utilizzando un'operazione cast, è possibile indicare al compilatore di convertire un valore di un tipo a un altro.Il compilatore genererà in alcuni casi un errore se i due tipi sono completamente indipendenti, ma in altri casi non genererà un errore anche se l'operazione non è indipendente dai tipi.Cast di utilizzo con parsimonia poiché qualsiasi conversione da un tipo a un altro è un'origine possibilità di errori di programma.Tuttavia, i cast vengono talvolta necessario e non tutti i cast sono altrettanto pericolosi.Un utilizzo efficace di un cast è quando il codice esegue una conversione verso un tipo di dati più piccolo e conoscere la conversione non venga indotto il programma a produrre risultati non corretti.In effetti, ciò indica al compilatore che si ritiene che si sta utilizzando e smettere di importunarvi con gli avvisi in questo argomento.Un altro utilizzo è eseguire il cast da una classe derivata puntatore a una classe di puntatore a base.Un altro utilizzo è eseguire il cast di const- ness di una variabile per passarla a una funzione che richiede un argomento non diconst.La maggior parte di queste operazioni includono cast alcuni rischi.

Nella programmazione di tipo C, lo stesso operatore di cast di tipo C viene utilizzato per tutti i tipi di cast.

(int) x; // old-style cast, old-style syntax
int(x); // old-style cast, functional syntax

L'operatore di cast di tipo C è identico all'operatore di chiamata () e quindi non appariscente nel codice e facile da ignorare.Entrambi sono errati in quanto sono difficili da riconoscere immediatamente o trovare e sono piuttosto diverse richiamare qualsiasi combinazione di static, di conste di reinterpret_cast.Comprendere cosa un cast di tipo obsoleto effettivamente fa può essere difficile e soggetto a errori.Per tutti questi motivi, quando un cast richiesto, è consigliabile utilizzare uno dei seguenti operatori di cast di C++, in alcuni casi molto più indipendenti dai tipi e che esprimono molto più esplicitamente lo scopo di programmazione:

  • static_cast, per i cast archiviati in fase di compilazione solo.static_cast restituisce un errore se rileva che si sta tentando di eseguire il cast tra tipi che sono completamente compatibili.È inoltre possibile utilizzarla per eseguire il cast tra un puntatore a base e puntatore a derivato, ma il compilatore non è in grado di riconoscere sempre se tali conversioni sono protette in fase di esecuzione.

       double d = 1.58947;
       int i = d;  // warning C4244 possible loss of data
       int j = static_cast<int>(d);       // No warning.
       string s = static_cast<string>(d); // Error C2440:cannot convert from
                                          // double to std:string
    
       // No error but not necessarily safe.
       Base* b = new Base();
       Derived* d2 = static_cast<Derived*>(b);
    

    Per ulteriori informazioni, vedere static_cast.

  • dynamic_cast, per la cassaforte, il ha archiviato i cast di puntatore a base al puntatore a un oggetto derivato.dynamic_cast è più sicuro di static_cast di downcast, ma il controllo automatico comporta un sovraccarico.

       Base* b = new Base();
    
       // Run-time check to determine whether b is actually a Derived*
       Derived* d3 = dynamic_cast<Derived*>(b);
    
       // If b was originally a Derived*, then d3 is a valid pointer.
       if(d3)
       {
          // Safe to call Derived method.
          cout << d3->DoSomethingMore() << endl;
       }
       else
       {
          // Run-time check failed.
          cout << "d3 is null" << endl;
       }
    
       //Output: d3 is null;
    

    Per ulteriori informazioni, vedere dynamic_cast.

  • const_cast, per eseguire il cast di const- ness di una variabile, o convertire una variabile non diconst per essere const.Buttando via const- il ness utilizzando questo operatore sono soggetto a errori che utilizza il cast di tipo c, ma con const-cast è meno probabile accidentalmente eseguire il cast.Talvolta è necessario eseguire il cast di const- ness di una variabile, ad esempio, per passare una variabile di const a una funzione che accetta un parametro non diconst.Nell'esempio seguente viene illustrato come effettuare questa operazione.

       void Func(double& d) { ... }
       void ConstCast()
       {
          const double pi = 3.14;
          Func(const_cast<double&>(pi)); //No error.
       }
    

    Per ulteriori informazioni, vedere const_cast.

  • reinterpret_cast, per i cast tra tipi come indipendenti pointer a int.

    [!NOTA]

    Questo operatore di cast non viene utilizzato spesso quanto gli altri e non è garantito che sia portabile ad altri compilatori.

    Nell'esempio seguente viene illustrato come reinterpret_cast differisce da static_cast.

       const char* str = "hello";
       int i = static_cast<int>(str);//error C2440: 'static_cast' : cannot
                                     // convert from 'const char *' to 'int'
       int j = (int)str; // C-style cast. Did the programmer really intend
                         // to do this?
       int k = reinterpret_cast<int>(str);// Programming intent is clear.
                                          // However, it is not 64-bit safe.
    

    Per ulteriori informazioni, vedere operatore di reinterpret_cast.

Vedere anche

Concetti

Sistema di tipi C++ (C++ moderno)

Altre risorse

Digitare di nuovo a C++ (C++ moderno)

Riferimenti al linguaggio C++

Riferimento della libreria C++ standard