2010-04-10 2 views
8

"Si vous renvoyez une valeur (pas une référence) de la fonction, puis la liez à une référence const dans la fonction appelante, sa durée de vie serait étendue à la portée de la fonction appelante."Comment sa durée de vie d'une valeur de retour est-elle étendue à la portée de la fonction appelante lorsqu'elle est liée à une référence const dans la fonction appelante?

Alors: CASE A

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

Renvoie une valeur de type const BoundingBox de la fonction GetBoundingBox()

variante I: (Bind à une référence const)

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

variante II: (Liez-le à une copie const)

const BoundingBox l_Bbox = l_pPlayer->GetBoundingBox(); 

Les deux fonctionnent bien et je ne vois pas l'objet l_Bbox sortir du cadre. (Bien que, je comprends dans la variante un, le constructeur de copie n'est pas appelé et est donc légèrement mieux que la variante II).

Aussi, pour comparaison, j'ai apporté les modifications suivantes.

CASE B

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

avec des variantes: Je

BoundingBox& l_Bbox = l_pPlayer->GetBoundingBox(); 

et II:

BoundingBox l_Bbox = l_pPlayer->GetBoundingBox(); 

L'objet l_Bbox ne marche toujours pas la portée. Comment "le lier à une référence const dans la fonction appelante, sa durée de vie serait étendue à la portée de la fonction appelante", étendre réellement la durée de vie de l'objet à la portée de la fonction appelante?

Est-ce que je manque quelque chose de trivial ici?

+1

Le fait qu'il semble fonctionner sur votre compilateur spécifique (c'est-à-dire, btw ...?) Ne signifie pas qu'il va fonctionner sur n'importe quel autre compilateur, ni qu'il est bien défini et légal. –

+0

@Peter: J'utilise MSVC 2008. Ce qui m'échappe c'est pourquoi/comment const peut-il avoir un effet sur la prolongation de la durée de vie? À mon avis, cela empêche quiconque d'apporter des modifications à l'objet (correction de const. Etc) et c'est tout. – brainydexter

+1

Votre variante I semble un peu drôle - votre compilateur accepte-t-il vraiment cela sans se plaindre?Il semble que vous initialisiez une référence à non-const avec un rvalue, ce qui ne devrait pas être autorisé. –

Répondre

8

Normalement, un objet temporaire (tel que celui retourné par un appel de fonction) a une durée de vie qui se prolonge jusqu'à la fin de la « renfermant l'expression ». Cependant, une liaison temporaire à une référence a généralement sa durée de vie 'promue' à la durée de vie de la référence (qui peut ou peut ne pas être la durée de vie de la fonction appelante), mais il y a quelques exceptions. Ceci est couvert par la norme en 12.2/5 « objets temporaires »:

Le temporaire auquel la référence est lié ou temporaire qui est l'objet complet à un sous-objet dont le temporaire est liée persiste pendant la durée de vie de la référence sauf comme spécifié ci-dessous. Une liaison temporaire à un membre de référence dans le ctor-initializer d'un constructeur (12.6.2) persiste jusqu'à la sortie du constructeur. Une liaison temporaire à un paramètre de référence dans un appel de fonction (5.2.2) persiste jusqu'à la fin de l'expression complète contenant l'appel.

Voir les éléments suivants pour plus d'informations:

Un exemple qui pourrait aider à visualiser ce qui se passe:

#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 ch affiche:

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

Le point est que lors du retour par valeur, la valeur est copiée dans la variable que vous affectez le résultat de la fonction. (Juste comme vous l'avez dit - le constructeur de copie est appelé). Aucune extension à vie, vous venez de créer un objet flambant neuf.

En revenant par référence, sous le capot vous venez de passer le pointeur sur la variable définie dans la fonction. Donc, un nouvel objet n'est pas créé, vous avez juste référence à lui en dehors de la fonction. Avec ce cas, la durée de vie d'une variable fonction-inside est étendue.

1

Habituellement, si vous retournez un objet en valeur d'une fonction, ledit objet sera détruit lorsque l'expression d'affectation est terminée:

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

Dans le cas que vous décrivez, la valeur retournée sera détruite plus tard sur, ce qui permet de l'utiliser:

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

Tout d'abord, la durée de vie de l'objet temporaire est étendu à la durée de vie de référence const qui est lié à elle, et non « à la portée de la fonction d'appel » (mais peut-être que ce que vous vouliez dire par cette étrange libellé "la portée de la fonction d'appel"). C'est ce que votre CASE A illustre, auquel vous attachez une référence const à une référence temporaire. Le temporaire continue de vivre tant que la référence vit. Lorsque la référence termine sa durée de vie, l'objet temporaire est également détruit.

Deuxièmement, votre CASE B est simplement mal formé, non compilable. A savoir, le

BoundingBox& l_Bbox = l_pPlayer->GetBoundingBox(); 

est illégal. Il est illégal en C++ d'attacher une référence non-const à un temporaire. Si votre compilateur le permet, il doit s'agir d'une excroissance/extension de votre compilateur, ce qui a peu à voir avec le langage C++.

Questions connexes