ProgrammazioneSviluppatore C++

Qual è la differenza tra l'inizializzazione diretta e indiretta degli oggetti in C++, e quali possono essere le conseguenze quando si lavora con tipi di base e tipi utente?

Supera i colloqui con l'assistente IA Hintsage

Risposta

In C++ si distinguono l'inizializzazione diretta (direct) e l'inizializzazione indiretta (copy) degli oggetti:

  • Inizializzazione diretta:

    MyClass obj(arg1, arg2); int x(5);

    Per le classi viene chiamato direttamente il costruttore appropriato.

  • Inizializzazione indiretta:

    MyClass obj = MyClass(arg1, arg2); int x = 5;

    Questo crea un oggetto temporaneo, quindi lo copia (o lo sposta) (usa il costruttore di copia/spostamento).

Differenze:

  1. Per i tipi di base il comportamento è identico. Per le classi — potrebbe esserci un insieme diverso di costruttori chiamati o persino un'inizializzazione vietata.
  2. La lista di inizializzazione dei membri non è sempre equivalente per i diversi modi di inizializzazione a causa dei requisiti del compilatore.
  3. Copy elision (ottimizzazione del compilatore per eliminare copie superflue) spesso annulla la differenza (ma non sempre, specialmente prima di C++17).

Esempio:

struct NonCopyable { NonCopyable(int) {} NonCopyable(const NonCopyable&) = delete; }; NonCopyable a(5); // OK: diretto NonCopyable b = NonCopyable(5); // OK con ottimizzazione, errore prima di C++17 senza di essa NonCopyable c = 5; // Errore: non c'è costruttore di copia

Domanda trabocchetto

"La copy-initialization può causare più chiamate ai costruttori rispetto alla direct-initialization, se si utilizza C++11 e un compilatore ottimizzante?"

Risposta: Teoricamente — sì. Senza copy elision la copy-initialization crea un oggetto temporaneo, quindi chiama il costruttore di copia o spostamento. La direct-initialization chiama sempre solo il costruttore principale. Tuttavia, i moderni compilatori con ottimizzazione attivata (C++17 e successivi) eliminano questa differenza grazie alla guaranteed copy elision.

Esempi di errori reali causati dalla mancata conoscenza delle complessità del tema


Storia

In un progetto finanziario è stata utilizzata la copy-initialization con un oggetto temporaneo, mentre il tipo richiesto non aveva un costruttore di copia. L'applicazione non si compilava su compilatori più vecchi (prima di C++17), anche se la direct-init funzionava.


Storia

In un'utilità di serializzazione dei dati i programmatori pensavano che direct-init e copy-init fossero equivalenti. Di conseguenza si verificavano copie superflue di grandi strutture e cali di prestazioni.


Storia

Durante l'inizializzazione di array globali di oggetti utente è stata utilizzata l'inizializzazione indiretta. Gli errori si manifestavano solo su una piattaforma, dove la copy elision non avveniva, e si verificavano chiamate implicite di costruttori superflui a causa delle peculiarità dell'ABI.