[debutant] Passage d'objet par reference
Le
Olivier BURELLI

Bonjour,
J'effectue mon apprentissage du C++ avec le livre suivant de J.
Liberty : le Langage C++
je suis interrogatif concernant le listing en fin de post.
si je comprends bien ce que j'ai appris jusqu'ici :
-dans la déclaration de la classe SimpleChat et sa methode, on constru=
it
une copie de l'objet par reference (alias) :
SimpleChat(SimpleChat&) , SimpleChat::SimpleChat(SimpleChat&)
dans la declaration et la methode de FonctionDeux on fait appel a des
pointeurs sur les objets :
SimpleChat* FonctionDeux (SimpleChat *leChat) on dereference bien le
pointeur par l'adresse de l'objet indique dans l'appel de la fonction :
FonctionDeux(Frisky&)
si je comprends bien :
FonctionUn renvoie par valeur la reference de l'objet pour la
construction de sa copie.
FonctionDeux renvoie le parametre par dereferencement du pointeur.
Question : Pourquoi dans le commentaire du listing ci-dessous pour
FonctionDeux est-il est précisé par reference ?
Question : Dans l'exposition de mon problème, ai-je bien assimilé=
la
notion de pointeur et reference ? J'ai l'impression que ce n'est pas
clair Si je m'embrouille un retour et mise en garde serait fort
sympatique.
Si quelqu'un veut bien m'apporter ses lumières, je lui en serai grÃ=
©e :)
Cdt,
Zolive.
[code]
//listing910.cpp - Passage de pointeur sur des objets
#include <iostream>
using namespace std;
class SimpleChat
{
public:
SimpleChat(); //
constructeur SimpleChat(SimpleChat&); //
constructeur de copie
~SimpleChat(); // destructeur };
SimpleChat::SimpleChat()
{
cout << "Constructeur de SimpleChat.." << endl;
}
SimpleChat::SimpleChat(SimpleChat&)
{
cout << "Constructeur de copie de SimpleChat" << endl;
}
SimpleChat::~SimpleChat()
{
cout << "Destructeur de SimpleChat" << endl;
}
SimpleChat FonctionUn (SimpleChat leChat);
SimpleChat* FonctionDeux (SimpleChat *leChat);
int main()
{
cout << "Creation d'un objet Chat" << endl;
SimpleChat Frisky;
cout << "Appel de FonctionUn" << endl;
FonctionUn(Frisky);
cout << "Appel de FonctionDeux" << endl;
FonctionDeux(&Frisky);
return 0;
}
// FonctionUn, passage par valeur
SimpleChat FonctionUn(SimpleChat leChat)
{
cout << "Retour de FonctionUn" << endl;
return leChat;
}
// FonctionDeux, passage par reference
SimpleChat* FonctionDeux(SimpleChat *leChat)
{
cout << "Retour de FonctionDeux" << endl;
return leChat;
}
[/code]
J'effectue mon apprentissage du C++ avec le livre suivant de J.
Liberty : le Langage C++
je suis interrogatif concernant le listing en fin de post.
si je comprends bien ce que j'ai appris jusqu'ici :
-dans la déclaration de la classe SimpleChat et sa methode, on constru=
it
une copie de l'objet par reference (alias) :
SimpleChat(SimpleChat&) , SimpleChat::SimpleChat(SimpleChat&)
dans la declaration et la methode de FonctionDeux on fait appel a des
pointeurs sur les objets :
SimpleChat* FonctionDeux (SimpleChat *leChat) on dereference bien le
pointeur par l'adresse de l'objet indique dans l'appel de la fonction :
FonctionDeux(Frisky&)
si je comprends bien :
FonctionUn renvoie par valeur la reference de l'objet pour la
construction de sa copie.
FonctionDeux renvoie le parametre par dereferencement du pointeur.
Question : Pourquoi dans le commentaire du listing ci-dessous pour
FonctionDeux est-il est précisé par reference ?
Question : Dans l'exposition de mon problème, ai-je bien assimilé=
la
notion de pointeur et reference ? J'ai l'impression que ce n'est pas
clair Si je m'embrouille un retour et mise en garde serait fort
sympatique.
Si quelqu'un veut bien m'apporter ses lumières, je lui en serai grÃ=
©e :)
Cdt,
Zolive.
[code]
//listing910.cpp - Passage de pointeur sur des objets
#include <iostream>
using namespace std;
class SimpleChat
{
public:
SimpleChat(); //
constructeur SimpleChat(SimpleChat&); //
constructeur de copie
~SimpleChat(); // destructeur };
SimpleChat::SimpleChat()
{
cout << "Constructeur de SimpleChat.." << endl;
}
SimpleChat::SimpleChat(SimpleChat&)
{
cout << "Constructeur de copie de SimpleChat" << endl;
}
SimpleChat::~SimpleChat()
{
cout << "Destructeur de SimpleChat" << endl;
}
SimpleChat FonctionUn (SimpleChat leChat);
SimpleChat* FonctionDeux (SimpleChat *leChat);
int main()
{
cout << "Creation d'un objet Chat" << endl;
SimpleChat Frisky;
cout << "Appel de FonctionUn" << endl;
FonctionUn(Frisky);
cout << "Appel de FonctionDeux" << endl;
FonctionDeux(&Frisky);
return 0;
}
// FonctionUn, passage par valeur
SimpleChat FonctionUn(SimpleChat leChat)
{
cout << "Retour de FonctionUn" << endl;
return leChat;
}
// FonctionDeux, passage par reference
SimpleChat* FonctionDeux(SimpleChat *leChat)
{
cout << "Retour de FonctionDeux" << endl;
return leChat;
}
[/code]
Plus que ca: le parametre est passe par valeur, et renvoye par valeur,
donc il y a au moins deux copies.
Ta fonction fait:
A f(A a)
^ ^^^ 1 copie
- 2e copie
{
return a;
}
f(a);
(meme si l'appel ne s'en sert pas).
C'est clairement une erreur de commentaire.
Si le code suivant est extrait du bouquin sur lequel tu apprends, change
de bouquins: ces exemples me semblent fortement confusogenes.
Ca me parait assez idiot de montrer des exemples qui font ET du passage
de parametres par valeur, ET du retour de parametre par valeur, surtout sans
utiliser la valeur de retour ! ca fait plein de concepts d'un coup.
Normalement n C++ on peut passer un certain temps sans croiser de pointeurs,
ceux-ci commencent a etre necessaires bien plus tard qu'en C.
Faudrait que tu nous dises ce qu'il y a dans ton bouquin sur le passage
par valeur et par reference. Si l'auteur attaque directement avec une
classe, un constructeur de copie, et des pointeurs, ca ne me surprend pas
outre mesure que tu sois perdu.
Pour plus te repondre, faudra sans doute des questions et des exemples
plus simples, qui ne melangent pas plein de concepts d'un coup.
Effectivement, c'est le merdier.
À ma connaissance, le seul bouquin valable pour enseigner le C++ aux
débutants est Accelerated C++, de Koenig & Moo.
D'autant que ça ne servira pas avant un moment. Dans la plupart des
cas (surtout au début), quand on est tenté d'utiliser un constructeur
de copie, c'est qu'on fait beaucoup trop compliqué, ou bien qu'un
réinvente la roue (typiquement, on reprogramme à la main un pointeur
intelligent).
Je suis assez satisfait de ce livre (le 3eme), bien qu'il y ait
quelques coquilles dans les codes suite à la traduction. Finalement
j'arrive à les corriger.
pour être concis :
Le chapitre en question, References :
-nature et role des references,
-differences entre reference et pointeurs
-creation et utilisation des references, passage par réference de
valeurs et d'objets a des fonctions et recuperation des valeurs
renvoyees
je cite :
dans le chapitre precedent on apprends a gerer des objets sur le tas a
s'y referer de facon indirecte. Ce chapitre presente quasiment les
memes fonctionalités des pointeurs mais avec une syntaxe plus simple.
Le passage par valeur d'un objet a une fonction implique une copie de
cet objet. Lorsque la fonction appelée renvoie un objet, une autre
copie a lieu
ces objets etant stockes dans la pile ces operations prennent du temps
et consommme de la memoire, ces couts sont negligeables pour les petits
objets comme les entiers.
En revanche il ne le sont plus pour les gros objets. La taille de cet
objet correspondant a la somme de toutes les variables membre qui la
composent.
l'auteur expose la problématique des classe, en abordant succinctement
le constructeur de copie qui sera aborde au chapitre suivant. le
theoreme "constructeur de copie" etant ; celui -ci est appele a chaque
fois qu'une copie temporaire de l'objet est place sur la pile.
Lorsque cet objet temporaire est detruit a la fin de l'execution de la
fonction, son destructeur est appelé. Si la fonction renvoie un objet
par valeur, il faut egalement faire copie de cet objet et le detruire.
Avec des objets volumineux ces appels de constructeurs et destructeurs
peuvent avoir un impact sur la vitesse d execution du programme et son
utilisation de la memoire.
pour illustrer cela l'auteur presentait le listing impliqué, le but
montrer la frequence des appels du constructeur / destructeur de copie.
C'est le 11 eme code démonstratif du chapitre, c'est le commentaire qui
ne collait pas avec ce que j'essayais de comprendre, d'où ma question.
Je n'osais pas rejeter la faute sur l'auteur ni le traducteur quand
même :p
La seconde, me permettait par retour de lecture du post, de savoir
si je me faisais comprendre, donc avais bien assimilé ce dont j
exposais.
l'objectif est atteint, je retourne à ma forge... je le reprendrais
apres avoir aborde les constructeurs de copie au chapitre suivant.
Merci pour tes lumieres :)
Oui, encore que le compilo a le droit d'optimiser des copies, et heureusement
encore.
Deux trois points vraiment importants sur le sujet:
- on differencie en premier approche les objets C++ en "classes concretes":
ce sont des petits objets (point dans le plan, date) qui vont avoir une
semantique de valeur. On ne se prive pas pour en placer directement sur la
pile, ou pour en passer directement aux fonctions. Le reste, ce sont les gros
objets, souvent concus pour l'heritage, et ceux-la seront passes par reference
et possedent souvent leur implementation 'des 4' fonctions: constructeur
par defaut, constructeur de copie, operateur d'affectation, et destructeur.
Dans beaucoup de cas modernes, on delegue: les smart-pointer de boost
permettent par exemple de ne rien avoir a implementer de tout ca, et juste
composer les champs necessaires.
- pour beaucoup d'objets, on va donc privilegier le passage de parametres
par reference. Il est alors vital d'etre "const-correct" (bien annoter tous
les parametres qui doivent etre const d'un const), d'abord, parce que ca
documente la fonction et que ca evite les erreurs stupides (ben oui, un
passage par reference, ca permet de faire des effets de bord ignobles), mais
aussi, parce que les temporaires ne peuvent etre passes que par parametres
const, e.g., f(A(5)) ne fonctionnera que si f(const A&) et pas f(A&).
- un cas ou les references ne sont pas appropries, c'est lorsqu'au final
il faut vraiment creer, un nouveau objet, le classique etant operator+,
par exemple, qui aura souvent comme implementation (je laisse de cote
les techniques d'expression-value a la Veldhuizen):
A operator+(const A& a, const A& b)
{
A c(a);
c += b;
return c;
}
Pas le choix, faut construire l'objet, et donc faire la copie. Mais un
compilo bien foutu disposera de "return value optimisation", et donc construira
c directement en place pour eviter une copie.
Mes petits camarades completeront...
Fabien LE LEZ
Je comprends, néanmoins je trouve que ce livre me convient assez. Les
Classes ont fait l'objet d'un chapitre, les pointeurs aussi, le
constructeur de copie est abordé dans le chapitre suivant.
ce code est le 10 eme de ce chapitre sur les références l'auteur
souhaitait présenter la frequence des appels du constructeur de copie
et sa lourdeur inhérente pour la gestion de memoire. Le but apparement
est de faire prendre conscience au lecteur de l'efficacité du passage de
paramètre par référence.
Actuellement, je garde le rythme du livre. Une fois lu en
entier où lors de mes besoins d'assimilation, je n'hésiterai pas à
effectuer des retours arrière.
Après il faudra que je forge, pour essayer de devenir un programmeur :)
Merci pour ces commentaires.
Bon, ben j'ai encore plus envie de te dire de jeter ton bouquin aux orties.
Les exemples te montrent un constructeur de copie, te parlent de passage
par reference, alors qu'ils n'ont pas aborde les points dont je parle
(specifiquement, les 4 methodes qui vont ensemble). Et ca parle de pointeur.
Desole, mais ca ressemble fort a un bouquin de C++ ecrit par quelqu'un qui
a fait beaucoup de C et qui ne pense pas en C++.
Un vrai bon bouquin de C++ ne va pas presenter les choses dans le meme
ordre.
(Marc Espie) wrote:
Ouhh la la,
le trois quart des choses exprimées est abstrait pour moi :) je tag
précieusement ce post pour, un jour ou cela devient comprehensible je
le retrouve. L'héritage et le polymorphysme ne sont abordé
respectivement que dans 3 et 5 chapitres.
J'en ai compris le principe, cela ressemble a la fin de mon chapitre...
Mon problème de départ n'était que le commentaire. Merci pou r tous ces
eclaircissements :)
À moins que tu aies une raison particulière pour apprendre le C++, ce
n'est pas le choix de langage que j'aurais fait pour écrire un logiciel
de finance personnelle.
Si le but c'est ça, il vaudrait mieux choisir un langage qui met moins
de batons dans les roues.
Par exemple, Common Lisp. (Je comprendrais aussi que l'on choisisse
Python ou Ruby, mais Common Lisp est meilleur).
Par exemple, à l'université de Bordeaux, les étudiants travaillent sur
un projet de comptabilité en Common Lisp:
git clone http://dept-info.labri.fr/~strandh/Compta
http://dept-info.labri.fr/~idurand/enseignement/PP3/pp.pdf
http://cliki.net/ pour plus d'informations.
http://cliki.net/Performance
--
__Pascal Bourguignon__ http://www.informatimago.com/
A bad day in () is better than a good day in {}.
Tiens, y'avait longtemps.
Je ne connais pas cet ouvrage, il est possible qu'il y ait un biais dans
ton approche. On t'a deja conseille "accelerated C++" qui est a mon avis
egalement un excellent bouquin d'apprentissage du C++.