Fonction en C ++ renvoie par valeur ou par référence?

Lorsqu’une fonction (appelée) renvoie une quantité à la fonction appelant, est-elle renvoyée par valeur ou par référence?

Le problème est que j’ai écrit une fonction qui construit un très grand vecteur d’appel. Je veux renvoyer ce gros vecteur à la fonction appelante ((dans ce cas, main() ) par référence constante afin que je puisse effectuer d’autres traitements sur celui-ci.

J’étais dans le doute parce qu’on m’a dit que lorsqu’une fonction C ++ revient et se termine, toutes les variables / mémoire associées à cette fonction sont supprimées.

 struct node{ ssortingng key; int pnum; node* ptr; } vector< vector > myfun1(/*Some arguments*/) { /*Build the vector of vectors. Call it V*/ return v; } int main(void) { a=myfun1(/* Some arguments */) } 

    Les fonctions C ++ peuvent renvoyer par valeur, par référence (mais ne renvoient pas une variable locale par référence), ou par un pointeur (encore une fois, ne retournez pas un local par un pointeur).

    Lors du retour par valeur, le compilateur peut souvent faire des optimisations qui le rendent aussi rapide que le retour par référence, sans le problème des références en suspens. Ces optimisations sont communément appelées “Optimisation de la valeur de retour (RVO)” et / ou “Optimisation de la valeur de retour nommée (NRVO)”.

    Un autre moyen pour l’appelant de fournir un vecteur vide (par référence) et de le renseigner par la fonction. Dans ce cas, il n’a rien à renvoyer.

    Vous devriez absolument lire ce billet: Want Speed? Pass par valeur.

    Par défaut, tout ce qui est en C / C ++ est passé par valeur, y compris le type de retour, comme dans l’exemple ci-dessous:

     T foo() ; 

    En C ++, où les types sont généralement considérés comme des types valeur (c’est-à-dire qu’ils se comportent comme double types int ou double ), la copie supplémentaire peut être coûteuse si la construction / destruction de l’object n’est pas sortingviale.

    Avec C ++ 03

    Si vous souhaitez retourner par référence ou par pointeur, vous devez modifier le type de retour en:

     T & foo() ; // return a reference T * foo() ; // return a pointer 

    mais dans les deux cas, vous devez vous assurer que l’object renvoyé existe toujours après le retour . Par exemple, si l’object renvoyé a été alloué sur une stack dans le corps de la fonction, l’object sera détruit et, par conséquent, sa référence / pointeur ne sera pas valide.

    Si vous ne pouvez pas garantir que l’object existe toujours après le retour, votre seule solution consiste à:

    1. accepter le coût d’une copie supplémentaire et espérer une optimisation de la valeur de retour
    2. passez à la place une variable par référence en tant que paramètre de la fonction, comme suit:

    void foo(T & t) ;

    De cette façon, dans la fonction, vous définissez la valeur t comme il convient, et après le retour de la fonction, vous obtenez votre résultat.

    Avec C ++ 11

    Maintenant, si vous avez la chance de travailler avec C ++ 0x / C ++ 11, c’est-à-dire avec un compilateur qui prend en charge les références r / valeurs sémantiques , si votre object a le bon constructeur / opérateur (si votre object arrive à partir de la bibliothèque standard, alors ça va), la copie temporaire supplémentaire sera optimisée, et vous pouvez conserver la notation:

     T foo() ; 

    Sachant que le compilateur ne générera pas une valeur temporaire inutile.

    Il est renvoyé par tout ce que vous déclarez comme type de retour. vector f(); et vector& f(); retourne par valeur et référence respectivement. Cependant, renvoyer une référence à une variable locale dans la fonction serait une grave erreur, car celle-ci aurait été supprimée lorsque la scope de la fonction sera fermée.

    Pour obtenir de bons conseils sur la manière de retourner efficacement des vecteurs volumineux à partir d’une fonction, consultez cette question (en fait, c’est une copie de celle-ci).

    La fonction retournera ce que vous lui dites de revenir. Si vous souhaitez renvoyer un vector , il sera alors copié dans la variable en attente par l’appelant. À moins que vous ne capturiez ce résultat par référence const, auquel cas il n’est pas nécessaire de le copier. Certaines optimisations permettent aux fonctions d’éviter cette construction de copie supplémentaire en plaçant le résultat dans l’object qui contiendra la valeur de retour. Vous devriez lire ceci avant de changer votre conception pour la performance:

    http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/

    C ++ peut renvoyer soit par référence, soit par valeur. Si vous souhaitez renvoyer une référence, vous devez l’indiquer dans le type de retour:

     std::vector my_func(); // returns value std::vector& my_func(); // returns reference std::vector const& my_func(); // returns constant reference 

    Toutes les variables locales (stack) créées à l’intérieur d’une fonction sont détruites au retour de la fonction. Cela signifie que vous ne devez absolument pas renvoyer les sections locales par référence ou référence const (ou des pointeurs vers elles). Si vous renvoyez le vecteur en valeur, il peut être copié avant la destruction du local, ce qui peut coûter cher. (Certains types d’optimisations appelées «optimisation de la valeur de retour» peuvent parfois supprimer la copie, mais cela n’entre pas dans le cadre de cette question. Il n’est pas toujours facile de dire si l’optimisation aura lieu sur un morceau de code particulier.)

    Si vous voulez “créer” un grand vecteur à l’intérieur d’une fonction et le renvoyer sans le copier, le plus simple consiste à transmettre le vecteur à la fonction en tant que paramètre de référence:

     void fill_vector(std::vector &vec) { // fill "vec" and don't return anything... } 

    Notez également que dans la nouvelle version récemment ratifiée du standard C ++ (connu sous le nom de C ++ 0x ou C ++ 11), le retour d’un vecteur local valeur par une fonction ne copiera pas le vecteur en question, il sera efficacement déplacé dans son nouveau emplacement. Le code utilisé est identique au code des versions précédentes de C ++ qui pourraient être forcés de copier le vecteur. Vérifiez auprès de votre compilateur s’il prend en charge la “sémantique de déplacement” (la partie de la norme C ++ 11 qui rend cela possible).

    Comme la plupart des choses en C ++, la réponse est “cela dépend de la façon dont vous avez défini la fonction”.

    La valeur par défaut pour la langue est return-by-value. Un simple appel comme “double f ()” va toujours renvoyer le nombre à virgule flottante par valeur. Cependant, vous POUVEZ retourner des valeurs par pointeur ou par référence; vous ajoutez simplement les symboles supplémentaires ‘&’ ou ‘*’ au type de retour:

     // Return by pointer (*) T* f(); // Return by reference (a single '&') T& f(); 

    Cependant, ceux-ci sont ridiculement dangereux dans de nombreuses situations. Si la valeur renvoyée par la fonction a été déclarée dans la fonction, la référence ou le pointeur renvoyé pointera vers une mémoire aléatoire au lieu de données valides. Même si vous pouvez garantir que les données pointées sont toujours présentes, ce type de retour est généralement plus gênant qu’il ne vaut la peine, étant donné les optimisations que tous les compilateurs C ++ modernes feront pour vous. Le moyen idiomatique et sûr de retourner quelque chose par référence est de passer une référence nommée en tant que paramètre:

     // Return by 'parameter' (a logical reference return) void f(T& output); 

    Maintenant, la sortie a un vrai nom, et nous soaps qu’elle survivra à l’appel car elle doit exister avant que l’appel à ‘f’ soit même effectué. C’est un motif que vous verrez souvent en C ++, en particulier pour des tâches telles que le remplissage d’un fichier STL std :: vector. C’est moche, mais jusqu’à l’avènement de C ++ 11, il était souvent plus rapide que de simplement renvoyer le vecteur par valeur. Maintenant que le retour par valeur est à la fois plus simple et plus rapide, même pour de nombreux types complexes, vous ne verrez probablement pas beaucoup de fonctions suivre le modèle de paramètre de retour de référence en dehors des bibliothèques plus anciennes.

    Toutes les variables définies sur la stack sont nettoyées à la sortie. Pour renvoyer une variable, vous devez l’allouer sur le tas, ce que vous faites avec le nouveau mot clé (ou malloc).

    Les classes et les structures sont transmises en tant que pointeurs, tandis que les types primitifs sont transmises en tant que valeurs.