2009-10-09 15 views
5

Ho il seguente codice:Due modi di chiamare predefinita costruttore

struct B 
{ 
//B() {} 
int x; 
int y; 
}; 

void print(const B &b) 
{ 
std::cout<<"x:"<<b.x<<std::endl; 
std::cout<<"y:"<<b.y<<std::endl; 
std::cout<<"--------"<<std::endl; 
} 

int main() 
{ 
B b1 = B(); //init1 
B b2; //init2 

print(b1); 
print(b2); 

return 0; 
} 

Quando inizio programma (VS2008, debug) ho il seguente output:

x:0 
y:0 
-------- 
x:-858993460 
y:-858993460 
-------- 

Come si può vedere b1. x e b1.y hanno valore 0. perché? Qual è la differenza tra init1 e init2?

Quando ho Decommentare B costruttore ho il seguente output:

x:-858993460 
y:-858993460 
-------- 
x:-858993460 
y:-858993460 
-------- 

Qualcuno può spiegare il motivo di questo comportamento? Tnx in anticipo.

risposta

10

Il costruttore predefinito per i tipi POD lo riempie di zeri. Quando si definisce esplicitamente il proprio costruttore non si inizializza x e e si ottengono valori casuali (nel debug VS sono riempiti con valori esatti, ma nel rilascio saranno casuali).

È secondo C++ 03 standard 8.5/5:

< ...> Per valore-inizializzare un oggetto di tipo T significa:
- se T è un tipo di classe (clausola 9) con un costruttore dichiarato dall'utente (12.1), viene chiamato il costruttore predefinito per T (e l'inizializzazione è mal formata se T non ha un costruttore predefinito accessibile);
- se T è un tipo di classe non di unione senza un costruttore dichiarato dall'utente, quindi ogni membro di dati non statici e componente di classe base di T è inizializzato a valore;
- se T è un tipo di matrice, ogni elemento viene inizializzato in base al valore;
- in caso contrario, l'oggetto è inizializzato a zero.

B() è un valore di inizializzazione temporanea che verrà utilizzato in copia-inizializzazione b1.

In B b2 non c'è inizializzatore specificato per un oggetto, quindi secondo C++ 03 standard 8.5/9:

Se non è specificato alcun inizializzatore di un oggetto, e l'oggetto è di (tipo di classe non POD qualificata (o matrice di esso), l'oggetto deve essere inizializzato di default; se l'oggetto è di tipo const-qualified, il tipo di classe sottostante deve avere un costruttore predefinito dichiarato dall'utente. Altrimenti, se non è specificato alcun inizializzatore per un oggetto non statico, l'oggetto e i suoi sottooggetti, se presenti, hanno un valore iniziale indeterminato; se l'oggetto o uno dei suoi sottooggetti sono di tipo const-qualified, il programma è mal formato.

Per ottenere zeri per b2 è possibile scrivere B b2 = {};.

+0

Come spiega i diversi risultati per 'b1' e' b2'? – sth

+0

E l'ultimo caso per "inizializzazione predefinita" dovrebbe essere "altrimenti, non viene eseguita alcuna inizializzazione". Questa è la differenza cruciale qui. – sth

+1

@sth: No, l'inizializzazione predefinita deve inizializzare a zero altri oggetti, il punto cruciale è che i tipi POD con durata di archiviazione automatica senza un inizializzatore esplicito non sono nemmeno inizializzati di default. –

2

L'O sono spiegati da Kirill

L'altro valore: -858.993.460 è 0xCCCCCCCC, che è il valore predefinito per la memoria non inizializzata in pila di debug di VC costruisce.

Non inizializzato heap valore di memoria predefinito 0xCDCDCDCD. E, naturalmente, nelle versioni di rilascio, i contenuti predefiniti sono casuali.

Ora, devo ammettere che non sapevo che fosse legale chiamare un agente direttamente come fai nel tuo esempio! E avrei giurato che questo è illegale ...

+0

nitpicking: 0xCC è il valore predefinito per la memoria non inizializzata ** stack ** in VC debug build. La memoria ** heap ** non inizializzata conterrà 0xCD – sbk

0

ho provato su vc6 e Visual Studio 2005 sto ottenendo sotto risultato in entrambi: Potresti per favore post disassemblare il codice generato, Come sotto ho postato il codice di disassemblaggio per il 2005

x: -858993460

y: -858993460


x: -858993460

y: -858.993.460


B b1 = B(); //init1 
0043DEDE lea   ecx,[b1] 
0043DEE1 call  B::B (43ADD9h) 
B b2; //init2 
0043DEE6 lea   ecx,[b2] 
0043DEE9 call  B::B (43ADD9h) 

print(b1); 
0043DEEE lea   eax,[b1] 
0043DEF1 push  eax 
0043DEF2 call  print (43A302h) 
0043DEF7 add   esp,4 
print(b2); 
0043DEFA lea   eax,[b2] 
0043DEFD push  eax 
0043DEFE call  print (43A302h) 
0043DF03 add   esp,4 
+0

Quindi, qual è la ragione della differenza? Gli operatori di assegnazione sono fuori tema in quanto non vengono chiamati. Anche i distruttori sono fuori tema. C'è molto testo nel tuo post, ma nessuna risposta chiara. –

+0

@ Alex, grazie per il commento, ho assunto che opeator = è stato chiamato, ma dopo il tuo commento ho controllato che non è stato chiamato affatto, mi permetta di fare qualche test, risponderò presto :) – Satbir

3

Questo è il valore di inizializzazione-versus nessun inizializzazione. Se si scrive

B b1 = B(); 

si ottiene copia-inizializzazione con un "valore-inizializzato" temporaneo - B(). L'inizializzazione del valore degli oggetti di tipo classe richiama un costruttore definito dall'utente (se esistente) o in altro modo inizializza i membri. L'inizializzazione del valore degli oggetti di tipo scalare equivale all'inizializzazione zero. Quando si dichiara il proprio costruttore che non fa nulla, i membri scalari non vengono inizializzati.

B b1; 

è un'inizializzazione di default o nessuna inizializzazione (a seconda di B).

Le esatte regole di inizializzazione sono piuttosto complicate. Vedere la sezione standard C++ 8.5 "Initializer".

5

In entrambi i casi, questa affermazione definisce b1 e copia-intializes da un valore-inizializzato temporanea B oggetto .

B b1 = B(); 

Quando B non ha un costruttore dall'utente dichiarato, valore-inizializzazione provoca richiamo membri B s' essere valore initalized, e per i tipi semplici, come int, questo mezzo inizializzazione a zero.

Quando B ha un costruttore dichiarato dall'utente, inizializzazione del valore tenta di chiamare il costruttore predefinito. Se i membri x e non sono elencati nell'elenco di inizializzazione del costruttore, vengono lasciati non inizializzati.

B b2; 

Nelle funzioni, oggetti locali di POD-tipo senza un inizializzatore sono lasciati inizializzata.Quando non si definisce un costruttore per B, si tratta di una classe POD, pertanto ciò si applica ei valori di b2.x e b2.y hanno valori indeterminati.

Se l'oggetto è di non-POD tipo di classe, allora è default-inizializzata, ma se ciò richiede un costruttore che lascia i suoi membri inizializzato allora questo fa differenza.

+0

Vuoi dire che nel primo caso vengono chiamati entrambi i costruttori di copia e di default? –

+0

Nominalmente, sì, ma al compilatore è permesso di ottimizzare la copia. In questo caso potrebbe direttamente _valore-inizializzare_ 'b1'. Qualunque cosa il compilatore scelga di fare, l'effetto osservabile (al programma) deve essere lo stesso. –

+0

Grazie per la spiegazione. –

Problemi correlati