Quand passe-t-on les arguments par référence ou par pointeur ?
1) Pour modifier les variables locales de la fonction appelante :
Une référence (ou un pointeur) permet à la fonction appelée de modifier une variable locale de la fonction appelante. Par exemple, considérons l'exemple de programme suivant où fun() est capable de modifier la variable locale x de main().
void fun(int &x) {
x = 20;
}
int main() {
int x = 10;
fun(x);
cout<<"New value of x is "<<x;
return 0;
}
Sortie : La nouvelle valeur de x est 20
2) Pour faire passer des arguments de grande taille :
Si un argument est grand, le passage par référence (ou pointeur) est plus efficace car seule une adresse est réellement passée, et non l'objet entier. Par exemple, considérons la classe Employé suivante et une fonction printEmpDetails() qui imprime les détails de l'employé.
class Employee {
private:
string name;
string desig;
// More attributes and operations
};
void printEmpDetails(Employee emp) {
cout<<emp.getName();
cout<<emp.getDesig();
// Print more attributes
}
Le problème avec le code ci-dessus est le suivant : chaque fois que printEmpDetails() est appelé, un nouvel objet Employee est construit, ce qui implique la création d'une copie de tous les membres de données. Une meilleure implémentation serait donc de passer Employee comme une référence.
void printEmpDetails(const Employee &emp) {
cout<<emp.getName();
cout<<emp.getDesig();
// Print more attributes
}
Ce point n'est valable que pour les variables de type struct et class, car nous n'obtenons aucun avantage en termes d'efficacité pour les types de base comme int, char, etc.
3) Pour éviter le découpage des objets :
Si nous passons un objet de sous-classe à une fonction qui attend un objet de super-classe alors l'objet passé est tranché s'il est passé par valeur. Par exemple, considérons le programme suivant, il imprime "This is Pet Class".
#include <iostream>
#include<string>
using namespace std;
class Pet {
public:
virtual string getDescription() const {
return "This is Pet class";
}
};
class Dog : public Pet {
public:
virtual string getDescription() const {
return "This is Dog class";
}
};
void describe(Pet p) { // Slices the derived class object
cout<<p.getDescription()<<endl;
}
int main() {
Dog d;
describe(d);
return 0;
}
Sortie : Voici la Classe des animaux
Si nous utilisons le passage par référence dans le programme ci-dessus, il imprime correctement "This is Dog Class". Voir le programme modifié suivant.
#include <iostream>
#include<string>
using namespace std;
class Pet {
public:
virtual string getDescription() const {
return "This is Pet class";
}
};
class Dog : public Pet {
public:
virtual string getDescription() const {
return "This is Dog class";
}
};
void describe(const Pet &p) { // Doesn't slice the derived class object.
cout<<p.getDescription()<<endl;
}
int main() {
Dog d;
describe(d);
return 0;
}
Sortie : Voici la classe de chien
Ce point n'est pas non plus valable pour les types de données de base comme int, char, etc.
4) Pour réaliser le Polymorphisme à l'exécution dans une fonction
Nous pouvons rendre une fonction polymorphe en lui passant des objets comme référence (ou pointeur). Par exemple, dans le programme suivant, print() reçoit une référence à l'objet de classe de base. print() appelle la fonction de classe de base show() si l'objet de classe de base est passé, et la fonction de classe dérivée show() si l'objet de classe dérivée est passé.
#include<iostream>
using namespace std;
class base {
public:
virtual void show() { // Note the virtual keyword here
cout<<"In base \n";
}
};
class derived: public base {
public:
void show() {
cout<<"In derived \n";
}
};
// Since we pass b as reference, we achieve run time polymorphism here.
void print(base &b) {
b.show();
}
int main(void) {
base b;
derived d;
print(b);
print(d);
return 0;
}
Sortie : En base
En dérivé
23 votes
pass by reference ... which introduces all sorts of complicated questions around ownership and especially around memory management (in the event that the object is heap-allocated)
. Je ne comprends pas en quoi c'est compliqué ou problématique pour la propriété ? Peut-être ai-je manqué quelque chose ?1 votes
@iammilind : Un exemple tiré de mon expérience personnelle. Un fil a un objet chaîne. Il est passé à une fonction qui génère un autre thread, mais sans que l'appelant le sache, la fonction a pris la chaîne de caractères en tant que
const std::string&
et non une copie. Le premier fil s'est ensuite éteint...12 votes
@ZanLynx : Cela ressemble à une fonction qui n'a clairement jamais été conçue pour être appelée comme une fonction de fil.
5 votes
D'accord avec iammilind, je ne vois pas de problème. Le passage par référence constante devrait être votre défaut pour les "grands" objets, et par valeur pour les plus petits objets. Je mettrais la limite entre grand et petit à environ 16 octets (ou 4 pointeurs sur un système 32 bits).
3 votes
Herb Sutter's Retour aux sources ! Présentation des éléments essentiels du C++ moderne au CppCon a donné beaucoup de détails à ce sujet. Vidéo ici .
0 votes
En rapport : Comment passer des objets aux fonctions en C++ ?
0 votes
Disons que j'ai un argument de type "sink" (par exemple une chaîne de caractères à écraser à l'étape suivante) dans une fonction qui renverra disons des substrats de la chaîne de caractères passée. Si je le passe par valeur, il sera d'abord construit par copie (y compris l'allocation de mémoire et la copie de données), puis la sous-chaîne sera construite à partir de la copie. Voulez-vous dire que cette copie sera éliminée par le compilateur ?