2010-04-10 2 views
8

"Si devuelve un valor (no una referencia) de la función, luego lo vincula a una referencia constante en la función de llamada, su duración se extenderá al alcance de la función de llamada."¿Cómo se extiende su duración de un valor de retorno al alcance de la función de llamada cuando está vinculado a una referencia constante en la función de llamada?

Así: CASO A

const BoundingBox Player::GetBoundingBox(void) 
{ 
    return BoundingBox(&GetBoundingSphere()); 
} 

Devuelve un valor de tipo const BoundingBox de la función GetBoundingBox()

variante I: (enlazarlo a una referencia const)

const BoundingBox& l_Bbox = l_pPlayer->GetBoundingBox(); 

variante II: (Enlazarlo a una copia const)

const BoundingBox l_Bbox = l_pPlayer->GetBoundingBox(); 

Ambos funcionan bien y no veo el objeto l_Bbox fuera del alcance. (Aunque, entiendo en la variante, el constructor de copia no se llama y, por lo tanto, es ligeramente mejor que la variante II).

Además, como comparación, realicé los siguientes cambios.

CASO B

BoundingBox Player::GetBoundingBox(void) 
{ 
    return BoundingBox(&GetBoundingSphere()); 
} 

con variantes: I

BoundingBox& l_Bbox = l_pPlayer->GetBoundingBox(); 

y II:

BoundingBox l_Bbox = l_pPlayer->GetBoundingBox(); 

El objeto l_Bbox todavía no sale alcance. ¿Cómo "vincularlo a una referencia constante en la función de llamada, su duración se extendería al alcance de la función de llamada", realmente extender la vida útil del objeto al alcance de la función de llamada?

¿Falta algo trivial aquí?

+1

El hecho de que parece funcionar en su compilador específico (que es, por cierto ...?) No significa que vaya a funcionar en ningún otro compilador, ni que esté bien definido y sea legal. –

+0

@Peter: estoy usando MSVC 2008. Lo que se me escapa es por qué/cómo puede tener un efecto en la prolongación de la vida útil. En mi opinión, impide que alguien haga cambios en el objeto (const correctness, etc.) y eso es todo ... – brainydexter

+1

Su variante Me parece un poco gracioso. ¿Su compilador realmente lo acepta sin quejas?Parece que estás inicializando una referencia a no const con un valor r, que no debería permitirse. –

Respuesta

8

Normalmente un objeto temporal (tal como uno devuelto por una llamada de función) tiene un tiempo de vida que se extiende hasta el final de la "expresión que encierra". Sin embargo, un límite temporal a una referencia generalmente tiene su duración 'promovida' a la duración de la referencia (que puede ser o no la duración de la función de llamada), pero hay un par de excepciones. Esto está cubierto por la norma en 12.2/5 "objetos Temporal":

El temporal a la que se une la referencia o el temporal que es el objeto completa a un subobjeto de los cuales el temporal está obligado persiste durante toda la vida de la referencia excepto como se especifica a continuación. Un límite temporal a un miembro de referencia en un ctor-initializer (12.6.2) del constructor persiste hasta que el constructor sale. Un límite temporal a un parámetro de referencia en una llamada a función (5.2.2) persiste hasta la finalización de la expresión completa que contiene la llamada.

Véase lo siguiente para más información:

Un ejemplo que puede ayudar a visualizar lo que está pasando:

#include <iostream> 
#include <string> 

class foo { 
public: 
    foo(std::string const& n) : name(n) { 
     std::cout << "foo ctor - " << name + " created\n"; 
    }; 
    foo(foo const& other) : name(other.name + " copy") { 
     std::cout << "foo copy ctor - " << name + " created\n"; 
    }; 

    ~foo() { 
     std::cout << name + " destroyed\n"; 
    }; 

    std::string getname() const { return name; }; 
    foo getcopy() const { return foo(*this); }; 

private: 
    std::string name; 
}; 

std::ostream& operator<<(std::ostream& strm, foo const& f) { 
    strm << f.getname(); 
    return strm; 
} 


int main() 
{ 
    foo x("x"); 

    std::cout << x.getcopy() << std::endl; 

    std::cout << "note that the temp has already been destroyed\n\n\n"; 

    foo const& ref(x.getcopy()); 

    std::cout << ref << std::endl; 

    std::cout << "the temp won't be deleted until after this...\n\n"; 
    std::cout << "note that the temp has *not* been destroyed yet...\n\n"; 
} 

Whi pantallas CH:

foo ctor - x created 
foo copy ctor - x copy created 
x copy 
x copy destroyed 
note that the temp has already been destroyed 


foo copy ctor - x copy created 
x copy 
the temp won't be deleted until after this... 

note that the temp has *not* been destroyed yet... 

x copy destroyed 
x destroyed 
2

El punto es que al devolver por valor, el valor es copiado en la variable que está asignando el resultado de la función. (como dijiste, se llama al constructor de copias ). Sin extensión de por vida, solo crea un objeto nuevo.

Al regresar por referencia, debajo del capó acaba de pasar el puntero a la variable definida en la función. Por lo tanto, no se crea un objeto nuevo, solo tiene referencia fuera de la función. En ese caso, la vida útil de una variable dentro de la función se amplía.

1

Por lo general, si devuelve un objeto por valor de una función, dicho objeto será destruido cuando se acaba la expresión de asignación:

myclass X = getX(); // after copy constructor, the returned value is destroyed 
        // (but you still hold a copy in X) 

En el caso que usted describe, el valor devuelto será destruido más tarde en adelante, lo que le permite utilizarlo:

const myclass& X = getX(); 
cout << X.a << endl; // still can access the returned value, it's not destroyed 
4

En primer lugar, el tiempo de vida del objeto temporal consigue extenderse a la vida útil de referencia constante que está destinado a él, no "al alcance de la función de llamada" (aunque tal vez eso lo que quería decir con esa extraña redacción "el alcance de la función de llamada"). Esto es lo que ilustra tu CASE A, donde adjuntas una const referencia a un temporal. El temporal continúa viviendo mientras viva la referencia. Cuando la referencia finaliza su duración, el objeto temporal también se destruye.

En segundo lugar, su CASE B es simplemente mal formado, no compilable. A saber, el

BoundingBox& l_Bbox = l_pPlayer->GetBoundingBox(); 

es ilegal. Es ilegal en C++ adjuntar una referencia no constante a una temporal. Si su compilador lo permite, debe ser una peculiaridad/extensión de su compilador, que tiene poco que ver con el lenguaje C++.

Cuestiones relacionadas