2012-07-01 14 views
7

Quiero averiguar dónde se almacena mi objeto arrojado en la memoria. Así que escribí un pequeño programa para ello:¿Dónde está el objeto arrojado en 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 imprime:

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 

Parece que, cuando lanzo un objeto, primero se copia a otra pila que está muy lejos de la uno normal. ¿Es esto cierto? ¿Y por qué hay una distancia de 144 bytes entre los dos "marcos de pila de excepción"?

Respuesta

6

Cuando arrojas un objeto, de hecho se copia primero en una ubicación temporal. De lo contrario, desenrollar la pila la habría sacado del alcance. La captura de él y luego por referencia habría dado lugar a un comportamiento indefinido, así:

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

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

A menos a había sido copiado en alguna ubicación temporal, que tendría una referencia a una variable que ya no existe en el catch. Para que esto funcione, A debe tener un constructor de copia pública (a menos que esté usando VS, en cuyo caso it will use a private one as well...). Además, esta es una buena razón para atrapar por referencia; de lo contrario, tendrá dos construcciones de copia en lugar de una.

+0

¡No puedo creerlo! Creo que es normal atrapar un objeto arrojado por referencia. –

+0

@ Industrial-antidepressant, lo es, y debería - es perfectamente seguro debido al hecho de que el objeto arrojado se copia. Si no hubiera sido copiado, tendrías un problema. – eran

2

Parece que, cuando tiro un objeto, se copia primero en otra pila que está muy lejos de la normal. ¿Es esto cierto?

El test que se lanzó no existe cuando se detecta la excepción. Ese original test ya ha sido destruido. Por lo tanto, debe ser una copia y el objeto nuevo debe gestionarse por separado de los argumentos y las variables locales. En otras palabras, no en la pila.

¿Dónde vive? Eso depende de la implementación. Lo más probable es que sea la memoria dinámica (por ejemplo, el montón) que la implementación administra para usted.

¿Y por qué hay una distancia de 144 bytes entre los dos "marco de pila de excepción"?

La norma no indica cómo una implementación trata una excepción cuando se vuelve a lanzar en un bloque catch. Como el throw de una variable local debe necesariamente hacer una copia, la forma más fácil de implementar throw es hacer siempre una copia.

+0

También es una muy buena respuesta pero puedo seleccionar solo una –

Cuestiones relacionadas