Domanda semplice: le seguenti affermazioni sono equivalenti? o è la seconda a fare più cose implicite dietro le quinte (se sì, che cosa?)Sintassi del costruttore C++
myClass x(3);
myClass x = myClass(3);
Grazie!
Domanda semplice: le seguenti affermazioni sono equivalenti? o è la seconda a fare più cose implicite dietro le quinte (se sì, che cosa?)Sintassi del costruttore C++
myClass x(3);
myClass x = myClass(3);
Grazie!
Non sono completamente identici. Il primo è chiamato "inizializzazione diretta" mentre il secondo è chiamato "inizializzazione della copia".
Ora lo standard costituisce due regole. Il primo è per l'inizializzazione diretta e per l'inizializzazione della copia in cui l'inizializzatore è del tipo dell'oggetto inizializzato. La seconda regola è per l'inizializzazione della copia in altri casi.
Quindi, da quel punto di vista entrambi sono definiti in una - la prima - regola. Nel caso in cui si abbia l'inizializzazione della copia con lo stesso tipo, il compilatore può elidere una copia, quindi può costruire il temporaneo che si crea direttamente nell'oggetto inizializzato. Quindi puoi finire molto bene con lo stesso codice generato. Ma il costruttore di copie, anche se la copia è stata eliminata (ottimizzata), deve essere ancora disponibile. Se si dispone di un costruttore di copia privata, tale codice non è valido se il codice in cui appare non ha accesso ad esso.
Il secondo è chiamato copia-inizializzazione, perché se il tipo di inizializzatore è di tipo diverso, un oggetto temporaneo viene creato nel tentativo di convertire implicitamente il lato destro al lato sinistro:
myclass c = 3;
Il compilatore crea un oggetto temporaneo del tipo di myclass, quindi quando c'è un costruttore che accetta un int. Quindi inizializza l'oggetto con quello temporaneo. Anche in questo caso, il creato temporaneo può essere creato direttamente nell'oggetto inizializzato. È possibile seguire questi passaggi stampando i messaggi nei costruttori/distruttori della classe e utilizzando l'opzione -fno-elide-constructors
per GCC. Quindi non prova ad eludere le copie.
Su una nota a margine, il codice sopra non ha nulla a che fare con un operatore di assegnazione. In entrambi i casi, ciò che accade è un'inizializzazione.
Il secondo può o non può richiedere una costruzione di oggetti myclass
aggiuntiva se copia elision non è implementata dal compilatore. Tuttavia, la maggior parte dei costruttori ha una copia di elision attivata per impostazione predefinita anche senza alcun interruttore di ottimizzazione.
Nota Inizializzazione mentre la costruzione non chiama mai l'operatore di assegnazione.
sempre, tenere a mente:
assignment: an already present object gets a new value
initialization: a new object gets a value at the moment it is born.
Questo non ha nulla a che fare con NRVO. –
Intendevo l'elisione, non riuscivo a trovare il termine giusto in quel momento. Grazie. – dirkgently
Nella seconda, un oggetto temporaneo viene creato per primo e poi viene copiato nella oggetto x usando myClass del costruttore di copia. Quindi entrambi non sono la stessa cosa.
ho scritto quanto segue per cercare di
illustrare
capire cosa sta succedendo:
#include <iostream>
using namespace std;
class myClass
{
public:
myClass(int x)
{
this -> x = x;
cout << "int constructor called with value x = " << x << endl;
}
myClass(const myClass& mc)
{
cout << "copy constructor called with value = " << mc.x << endl;
x = mc.x;
}
myClass & operator = (const myClass & that)
{
cout << "assignment called" << endl;
if(this != &that)
{
x = that.x;
}
return *this;
}
private:
int x;
};
int main()
{
myClass x(3);
myClass y = myClass(3);
}
Quando compilo ed eseguire questo codice ottengo il seguente output:
$ ./a.out
int constructor called with value x = 3
int constructor called with value x = 3
Questo sarebbe sembrano per indicare che non vi è alcuna differenza tra le due chiamate effettuate nella funzione principale, ma che sarebbe sbagliato. Come indicato da litb, il costruttore di copie deve essere disponibile per il codice affinché questo codice funzioni, anche se in questo caso viene eliminato. Per provarlo, basta spostare il costruttore di copia nel codice sopra nella sezione privata della definizione della classe. Si dovrebbe vedere il seguente errore:
$ g++ myClass.cpp
myClass.cpp: In function ‘int main()’:
myClass.cpp:27: error: ‘myClass::myClass(const myClass&)’ is private
myClass.cpp:37: error: within this context
Si noti inoltre che l'operatore di assegnazione è mai chiamato.
Ma se rimuovo completamente il costruttore di copia da quel codice, funziona ancora. Che cosa implica? – Baltimark
Ciò implica che il compilatore sta inserendo un costruttore di copie predefinito per te. –
Certo, nessun problema. –
con VC9 "myclass c = 3" sembra chiamare semplicemente "myclass (int x)" direttamente, piuttosto che usare un oggetto myclass temporaneo. –
VC è rotto a questo proposito. Ha una regola simile a: "Usa l'inizializzazione diretta ove possibile". –
Grazie mille per la spiegazione dettagliata, in realtà ho letto Scott Meyer e Herb Sutter che hanno discusso di questo argomento, ma non sono riuscito a verificarlo tramite VC++ e GCC, perché per impostazione predefinita entrambi i compilatori elide il copy ctor addirittura disattivano l'ottimizzazione. L'opzione gcc -fno-elide-constructors è preziosa per me per osservare la semantica esatta senza alcuna ottimizzazione. Anche il fatto che "il copyctor deve essere disponibile" è molto utile. – zhaorufei