2009-07-18 14 views
6

Qualcuno potrebbe spiegarmi perché questo codice stampa solo "42" invece di "creato \ n42"?Perché questo codice stampa solo 42?

#include <iostream> 
#include <string> 
#include <memory> 

using namespace std; 

class MyClass 
{ 
public: 
    MyClass() {cout<<"created"<<endl;}; 
    int solution() {return 42;} 
    virtual ~MyClass() {}; 
}; 

int main(int argc, char *argv[]) 
{ 
    auto_ptr<MyClass> ptr; 
    cout<<ptr->solution()<<endl; 
    return 0; 
} 

BTW ho provato questo codice con valori diversi in soluzione e ho sempre ottenere il valore "giusto", in modo che non sembra essere un valore fortunato casuale.

+13

Il mio consiglio sarebbe di consultare la "Guida galattica per autostoppisti". – NoMoreZealots

risposta

27

Poiché presenta un comportamento non definito, è necessario il riferimento a un puntatore nullo.

Quando si dice:

auto_ptr<MyClass> ptr; 

si crea un autopointer che non punta a nulla. Ciò equivale a dire:

MyClass * ptr = NULL; 

Poi, quando si dice:

cout<<ptr->solution()<<endl; 
si

dereference questo puntatore nullo. Farlo non è definito in C++: per la tua implementazione, sembra funzionare.

+1

Quello era un interessante scambio di risposte accettate, haha. : P – GManNickG

+0

Ma il mio è stato il primo :-) –

+0

Oh non mi lamento :) Ho alzato il tuo dopo che ho presentato, ero lento sul sorteggio. – GManNickG

2

Perché non si conosce la questione alla risposta xD

Sembra che non stai chiamando il costruttore, giusto?

+0

Questo è quello che stavo pensando ... – CalebHC

21

std::auto_ptr non creerà automaticamente un oggetto per te. Cioè, ptr in main in quanto è inizializzato a null. Dereferenziare è un comportamento indefinito, e ti capita di essere fortunato e ottenere 42 come risultato.

Se effettivamente crea l'oggetto:

int main(int argc, char *argv[]) 
{ 
    auto_ptr<MyClass> ptr(new MyClass); 

    cout << ptr->solution() << endl; 

    return 0; 
} 

Si otterrà l'output che ci si aspetta.

+3

Ho sempre saputo che 42 era la risposta a tutto, ma non avevo mai pensato che anche quel puntatore nullo lo sapesse. Grazie! BTW perché questo non genera un errore di segmentazione? – rlazo

+2

Perché è quello che fa il comportamento non definito, potrebbe sembrare funzionare, o potrebbe riformattare il tuo computer. Probabilmente la ragione per cui ha funzionato è perché non hai effettivamente operato sui membri della classe. Il compilatore vede che stai accedendo a 'MyClass :: solution'. Inserisce 0 per il puntatore 'this', perché è quello che è, entra nella funzione, ottiene 42 come risultato e ritorna. Dai alla tua cls un 'risposta int' al membro privato ', imposta quella a 42 nel costruttore e restituiscilo in 'solution()' e dovresti vedere un crash, perché ora stai provando a usare il puntatore nullo 'this' . – GManNickG

+1

@rlazo: non genera un segfault perché la funzione di soluzione non accede a nessuna variabile membro, quindi il puntatore "questo" non viene utilizzato all'interno della funzione. –

2

Non si sta creando un'istanza dell'oggetto.
Si sta creando solo un puntatore intelligente.

Quando si chiama il metodo si de-fa riferimento a un puntatore NULL, così come Neil ha menzionato che si sta ora utilizzando un comportamento non definito. Ma dal momento che il tuo codice non tenta e accede a qualsiasi variabile membro, fortunatamente non si blocca.

Prova questo:

auto_ptr<MyClass> ptr(new MyClass); 
+0

Direi "sfortunatamente" piuttosto che "per fortuna". –

1

Perché ptr è inizializzato e sei fortunato. Si dovrebbe prima chiamare new per esso:

auto_ptr<MyClass> ptr(new MyClass); 
1

Non stai ricevendo un incidente perché il metodo "soluzione" non ha bisogno di utilizzare effettivamente i membri della classe. Se dovessi restituire un membro o qualcosa del genere, probabilmente avresti un incidente.

3

Innanzitutto, tenere presente che l'operatore -> di auto_ptr viene essenzialmente inoltrato al puntatore contenuto.Così, per questa discussione, il codice in main diventa equivalente a:

MyClass* ptr = NULL; 
cout << ptr->solution() << endl; 

Poi notare che i compilatori tendono a implementare funzioni membro in modi che agiscono molto funzioni molto terzi come se fossero con il puntatore this passato come un altro argomento della funzione. Quindi, dal punto di vista del compilatore corrente, il codice in main agisce come se fosse:

MyClass* ptr = NULL; 
cout << solution(ptr) << endl; 

con la soluzione scritta come:

int solution(MyClass* this) { return 42; } 

Nel qual caso diventa evidente il motivo per cui non c'era una crash.


Tuttavia come altri hanno già detto, questi sono dettagli interni come compilatori implementano C++, che non sono specificati dal linguaggio standard. Quindi in teoria questo codice potrebbe funzionare come descritto qui su un compilatore, ma si blocca o fa qualcos'altro interamente su un altro compilatore.

Ma in pratica, anche se la norma non garantisce questo comportamento, qualsiasi compilatore particolare potrebbe garantirla, se vogliono. Ad esempio: poiché MFC si basa su questo comportamento, è molto improbabile che Visual Studio smetterà mai di supportarlo. Ovviamente, dovresti ricercare ogni particolare compilatore in cui il tuo codice potrebbe essere usato per assicurarti che effettivamente garantisca questo comportamento.