2012-07-01 20 views
7

Voglio capire dove viene memorizzato il mio oggetto generato. Così ho scritto un piccolo programma per esso:Dove si trova l'oggetto lanciato in C++?

#include <iostream> 

#define print_dist() int a;\ 
    do { std::cout << __FUNCTION__ << "() a[" << (long)&a - (long)ptrMainStackBase << "]" << std::endl; } while (0) 

#define print_distx(x) \ 
    do { std::cout << __FUNCTION__ << "() " #x "[" << (long)&x - (long)ptrMainStackBase << "]" << std::endl; } while (0) 

#define print_distxy(x, y) \ 
    do { std::cout << __FUNCTION__ << "() " #x "(ex)[" << (long)&x - (long)y << "]" << std::endl; } while (0) 

class CTest 
{ 
    public: 
     CTest() 
     { std::cout << "CTest::CTest" << std::endl; } 

// private: 
     CTest(const CTest&) 
     { std::cout << "copy" << std::endl; } 
}; 
const CTest *ptrException; 
int *ptrMainStackBase; 

void Test2() 
{ 
    print_dist(); 
    CTest test; 
    print_distx(test); 
    std::cout << "&test=" << &test << std::endl; 
    throw test; 
} 

void Test1() 
{ 
    print_dist(); 
    try 
    { 
     Test2(); 
    } 
    catch (const CTest& test) 
    { 
     ptrException = &test; 
     print_dist(); 
     print_distx(test); 
     print_distxy(test, ptrException); 
     std::cout << "&test=" << &test << std::endl; 
     throw test; 
    } 
} 

int main() 
{ 
    int b; 
    ptrMainStackBase = &b; 
    print_dist(); 
    try 
    { 
     print_dist(); 
     Test1(); 
    } 
    catch (const CTest& test) 
    { 
     print_dist(); 
     print_distx(test); 
     print_distxy(test, ptrException); 
     std::cout << "&test=" << &test << std::endl; 
    } 

    return 0; 
} 

e stampe:

main() a[-4] 
main() a[-8] 
Test1() a[-64] 
Test2() a[-104] 
CTest::CTest 
Test2() test[-108] 
&test=0x7fffd3b21628 <- test created here on stack 
copy 
Test1() a[-68] 
Test1() test[-140736732956164] 
Test1() test(ex)[0] 
&test=0xb89090 <- and copied here 
copy 
main() a[-12] 
main() test[-140736732956020] 
main() test(ex)[144] 
&test=0xb89120 <- and here 

Sembra che, quando butto un oggetto, viene prima copiato in un'altra pila che è lontano dalla quello normale È vero? E perché ci sono 144 byte di distanza tra i due "frame dello stack di eccezioni"?

risposta

6

Quando si lancia un oggetto, questo viene effettivamente copiato prima in una posizione temporanea. Altrimenti, lo srotolamento dello stack l'avrebbe tolto dallo scope. Facendo poi facendo riferimento avrebbe comportato un comportamento indefinito, in questo modo:

void foo() { 
    A a; 
    throw a; 
} 

void bar() { 
    try { 
     foo(); 
    } catch (A& a) { 
     // use a 
    } 
} 

A meno a era stato copiato in una certa posizione temporanea, si avrebbe un riferimento a una variabile che non esiste più nel catch. Affinché questo funzioni, A deve avere un costruttore di copia pubblico (a meno che non si utilizzi VS, nel qual caso it will use a private one as well...). Inoltre, questa è una buona ragione per prendere come riferimento - altrimenti, avrai due costruzioni di copia invece di una.

+0

Non ci posso credere! Penso che sia normale prendere un oggetto lanciato per riferimento. –

+0

@ antidepressivo industriale, lo è, e dovrebbe - è perfettamente sicuro a causa del fatto che l'oggetto lanciato viene copiato. Se non fosse stato copiato, avresti un problema. – eran

2

Sembra che quando lancio un oggetto viene copiato prima su un'altra pila che è molto lontana da quella normale. È vero?

Il test che è stato lanciato non esiste quando viene rilevata l'eccezione. Quello originale test è già stato distrutto. Quindi deve essere una copia e il nuovo oggetto gestito separatamente dagli argomenti e dalle variabili locali. In altre parole, non in pila.

Dove vive? Questo dipende dall'implementazione. Molto probabilmente è la memoria dinamica (ad es. L'heap) che l'implementazione gestisce per te.

E perché ci sono 144 byte di distanza tra i due "frame di stack di eccezione"?

Lo standard non indica in che modo un'implementazione deve trattare un'eccezione quando viene nuovamente generata in un blocco catch. Poiché lo throw di una variabile locale deve necessariamente eseguire una copia, il modo più semplice per implementare throw consiste nel fare sempre una copia.

+0

È anche un'ottima risposta, ma posso selezionarne solo una –