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

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

Le truc c'est que j'ai écrit une fonction qui construit un très grand vecteur de quand on l'appelle. Je veux retour ce gros vecteur de la fonction d'appel , ( dans ce cas main() ) par référence constante afin que je puisse faire un traitement ultérieur.

j'étais dans le doute parce qu'on m'a dit que quand une fonction c++ revient et se termine, tous les variables/mémoire associée à cette fonction, obtenir essuyée.

struct node{

string key;
int pnum;
node* ptr;
}

vector< vector<node> > myfun1(/*Some arguments*/)
{

/*Build the vector of vectors. Call it V*/

return v;

}

int main(void)
{
a=myfun1(/* Some arguments */)
}
15
demandé sur Benjamin 2011-09-17 22:03:31

7 réponses

les fonctions C++ peuvent revenir par valeur, par référence (mais ne renvoient pas une variable locale par référence), ou par pointeur (encore une fois, ne renvoient pas un local par 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 pendantes. Ces optimisations sont communément appelées " Return Value Optimization (RVO)" et/ou "Named Return Value Optimization (NRVO)".

D'une autre façon à l'appelant de fournir un vecteur vide (par référence), et la fonction de la compléter. Alors il n'a pas besoin de rendre quoi que ce soit.

Vous avez certainement devraient lire ce blogue: Vous Voulez De La Vitesse? Le passage par valeur.

22
répondu Ben Voigt 2017-11-15 15:14:54

par défaut, tout 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 valeurs-types (i.e. ils se comportent comme int ou double types), la copie supplémentaire peut être coûteuse si la construction/destruction de l'objet n'est pas insignifiante.

Avec C++03

Si vous souhaitez retourner par référence ou par pointeur, vous devez changer le type de retour à soit:

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

mais dans les deux cas, vous devez vous assurer que l'objet retourné existe encore après le retour. Par exemple, si le produit retourné a été alloué sur la pile dans le corps de la fonction, l'objet sera détruit, et donc, sa référence/pointeur invalide.

Si vous ne pouvez pas garantir que l'objet existe toujours après le retour, votre seule solution est soit:

  1. accepter le coût d'une copie supplémentaire et de l'espoir Valeur De Retour D'Optimisation
  2. passer à la place une variable par référence comme paramètre à la fonction, comme dans ce qui suit:

void foo(T & t) ;

de Cette façon, à l'intérieur de la fonction, vous définissez t valeur si nécessaire, et après le retour de la fonction, vous avez 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 supporte r-valeurs références/sémantique de déplacement, si votre objet a le bon constructeur / opérateur (si votre objet vient de la bibliothèque standard, alors c'est ok), alors la copie temporaire supplémentaire sera optimisée, et vous pouvez garder la notation:

T foo() ;

sachant que le compilateur ne générera pas de valeur temporaire inutile.

21
répondu paercebal 2011-09-17 18:19:18

il est retourné par ce que vous déclarez le type de retour à être. vector<int> f(); et vector<int>& f(); retour par valeur et de référence, respectivement. Cependant, ce serait une grave erreur de renvoyer une référence à une variable locale dans la fonction qu'il aura été époustouflé quand la portée de la fonction des sorties.

pour de bons conseils sur la façon de retourner efficacement de grands vecteurs à partir d'une fonction, Voir cette question (en fait, c'est sans doute un double).

3
répondu timday 2017-05-23 12:32:14

la fonction retournera ce que vous lui direz de retourner. Si vous voulez retourner un vector, alors il sera copié dans la variable tenir en l'appelant. Sauf si vous capturez ce résultat par une référence à const, auquel cas il n'est pas nécessaire de le copier. Il y a des optimisations qui permettent aux fonctions d'éviter ce copy-constructon supplémentaire en plaçant le résultat dans l'objet qui conservera la valeur de retour. Vous devriez lire ceci avant de changer votre conception pour performance:

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

2
répondu K-ballo 2011-09-17 18:10:56

C++ peut revenir soit par référence soit par valeur. Si vous voulez retourner une référence, vous devez spécifier que le type de retour:

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

toutes les variables locales (stack) créées à l'intérieur d'une fonction sont détruites lorsque la fonction retourne. Cela signifie que vous ne devez absolument pas rendre les locaux par référence ou const référence (ou des pointeurs à eux). Si vous retournez le vecteur par valeur copier avant du local est détruit, ce qui pourrait être coûteux. (Certains types d'optimisations appelées "optimisation de la valeur de retour" peuvent parfois supprimer la copie, mais c'est hors du champ de cette question. Il n'est pas toujours facile de dire si l'optimisation va se passer sur un morceau de code.)

si vous voulez "créer" un grand vecteur à l'intérieur d'une fonction et le retourner ensuite sans copier, la manière la plus simple est de passer le vecteur à la fonction comme paramètre de référence:

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

notez Également que dans le récemment ratifié nouvelle version de la norme c++ (connu comme C++0x ou C++11) retournant un vecteur local par la valeur d'une fonction en fait, copier le vecteur, il sera efficace déplacé dans son nouvel emplacement. Le code qui fait cela ressemble identique de coder à partir des versions précédentes de C++ qui pourraient être forcées de copier le vecteur. Vérifiez avec votre compilateur s'il supporte "move semantics" (la partie du standard C++11 qui fait ceci possible.)

2
répondu SoapBox 2011-09-17 18:13:33

La valeur par défaut pour la langue, c'est le retour par valeur. Un simple appel comme "double f()" va toujours retourner 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, ils sont ridiculement dangereux dans de nombreuses situations. Si la valeur de la la fonction retourne a été déclarée dans la fonction, la référence retournée ou le pointeur pointera vers des déchets aléatoires au lieu de données valides. Même si vous pouvez garantir que les données pointées sont toujours présentes, ce genre de retour est généralement plus problématique qu'il n'en vaut la peine compte tenu des optimisations que tous les compilateurs C++ modernes feront pour vous. La façon idiomatique et sûre de retourner quelque chose par référence est de passer une référence nommée en paramètre:

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

maintenant la sortie a un réel et nous savons qu'il survivra à l'appel parce qu'il doit exister avant même que l'appel à 'f' ne soit fait. C'est un modèle que vous verrez souvent en C++, surtout pour des choses comme peupler un STD STL::vector. Son laid, mais jusqu'à L'avènement du C++11 Il était souvent plus rapide que de simplement retourner le vecteur en valeur. Maintenant que le retour par la valeur est à la fois plus simple et plus rapide même pour beaucoup de types complexes, vous ne verrez probablement pas beaucoup de fonctions suivant le modèle de paramètre de retour de référence en dehors de l'ancien bibliothèque.

0
répondu Zack Yezek 2014-01-30 06:36:03

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

les Classes et les structures sont passées en pointeurs, tandis que les types primitifs sont passés en valeurs.

-5
répondu nulvinge 2011-09-17 18:07:38