Question débile - fonctions membres C++

Question débile - fonctions membres C++ - C++ - Programmation

Marsh Posté le 10-02-2009 à 22:24:37    

Salut à tous,
 
Attention ma question va sans doute paraître très très basique à certains mais pitié ne vous moquez pas  :sweat:  Je précise dabord que j'ai bien sûr commencé par fair une recherche sur le forum, et dans le divers tutos que j'ai l'habitude de consulter en tant que grand débutant en C++, mais en vain. Je pense que c'est trop facile pour être mentionné !
 
Donc voilà j'en viens au fait : j'ai défini une classe Truc, avec une fonction membre Func1 :
 

Code :
  1. double Truc::Func1()
  2. {.....}


 
Maintenant je veux définir une autre méthode dans la même classe, qui utilise le résultat de la précédente, par exemple je veux calculer son carré ; j'ai essayé
 

Code :
  1. double Truc::Func2();
  2. {
  3. return( Func1() * Func1() );
  4. }


 
Mais ça n'a pas l'air de fonctionner. Quelle est la bonne manière de le faire ?
 
Merci d'avance pour votre réponse (et votre indulgence  :) ).

Reply

Marsh Posté le 10-02-2009 à 22:24:37   

Reply

Marsh Posté le 10-02-2009 à 22:36:31    

ton ; c'ets une erreur de frappe là ?
 
Sinon :
- ta methode est bien déclarée ET définie ?
 
montre du code complet stp

Reply

Marsh Posté le 10-02-2009 à 22:53:08    

Merci pour la réponse rapide. Oui bien sûr le ; dans la définition c'est une grosse faute de frappe   :D

 

Alors voilà un exemple de code complet :

 

Truc.h

Code :
  1. #include <cstdlib>
  2. #include <iostream>
  3. #include <string>
  4. #include <math.h>
  5. #ifndef DEF_TRUC
  6. #define DEF_TRUC
  7. using namespace std;
  8. class Truc
  9. {
  10.     public:
  11.         Truc();
  12.         double NormeAuCarre();
  13.         double Norme();
  14.     private:
  15.         double x;
  16.         double y;
  17. };
  18. #endif
 

Truc.cpp

Code :
  1. #include <cstdlib>
  2. #include <iostream>
  3. #include <string>
  4. #include <math.h>
  5. #include "Truc.h"
  6. Truc::Truc()
  7. {
  8.     x = 1;
  9.     y = 2;
  10. }
  11. double Truc::NormeAuCarre()
  12. {
  13.     return(x * x + y * y);
  14. }
  15. double Truc::Norme()
  16. {
  17.     return(sqrt(NormeAuCarre()));
  18. }
 

Main.cpp

Code :
  1. #include <cstdlib>
  2. #include <iostream>
  3. #include <string>
  4. #include <math.h>
  5. #include "Truc.h"
  6. using namespace std;
  7. int main(int argc, char *argv[])
  8. {
  9.     Truc point;
  10.     double n,n2;
  11.     n2 = point.NormeAuCarre(); // jusqu'ici ça marchait (sans avoir défini la méthode Norme)
  12.     n = point.Norme();
  13.     cout << "La norme au carré est " << n2 << endl;
  14.     system("PAUSE" )
  15.     return(0);
  16. }
 

Diagnostic ?


Message édité par mouletabille le 10-02-2009 à 22:53:58
Reply

Marsh Posté le 10-02-2009 à 22:55:05    

c'ets quoi ton compilo
apres, detail, tes methodes devriat etre const et using namespace dans un .h merite le pal


Message édité par Joel F le 10-02-2009 à 22:55:25
Reply

Marsh Posté le 10-02-2009 à 22:55:30    

On m'a parlé d'utiliser le pointeur this (ça m'a effectivement rappelé quelque chose), ça vous paraît une solution adaptée ? y a-t-il plus simple ? moins simple ?

Reply

Marsh Posté le 10-02-2009 à 23:02:59    

normalement t'en as pas besoin à moins d'utiliser un compilo foireux.
Quel est ton compilo :o

Reply

Marsh Posté le 10-02-2009 à 23:06:24    

Argh pardon monseigneur mais pitié pas le pal je corrige tout de suite ! pourquoi déjà au fait ? on m'a expliqué le problème que ça posait mais j'ai déjà oublié. OK pour les méthodes const, parce qu'elles ne modifient pas les attributs (right ?).
 
Je suis sous windaube XP avec Dev-C++ 4.9.9.2

Reply

Marsh Posté le 10-02-2009 à 23:16:07    

je pense que ca n'a rien à voir mais, dev cpp ets assez moche et utilsie un vieux gcc. Installe CodeBlocks

Reply

Marsh Posté le 10-02-2009 à 23:33:38    

J'avais installé CodeBlocks mais ça avait l'air moins simple à prendre en main que Dev-C++ ; cela dit si tu me dis que c'est mieux je veux bien tenter la migration.

Reply

Marsh Posté le 11-02-2009 à 00:14:48    

mouletabille a écrit :

J'avais installé CodeBlocks mais ça avait l'air moins simple à prendre en main que Dev-C++ ; cela dit si tu me dis que c'est mieux je veux bien tenter la migration.


Dev-C++ est pas mal pour commencer, mais perso j'ai vite eu des emmerdes avec ... n'hésite pas à utiliser un autre IDE ...


---------------
By bob.
Reply

Marsh Posté le 11-02-2009 à 00:14:48   

Reply

Marsh Posté le 11-02-2009 à 01:30:35    

Les gars vous me portez la poisse je viens d'avoir mes premiers ennuis avec Dev-C++ ! Problème avec l'éditeur de texte plus précisément, quand je copiais/collais des lignes ça écrasait les lignes préexistantes au lieu de les décaler vers le bas (pourtant je n'étais pas en mode "inser" au niveau du clavier) et après impossible de sélectionner des portions de lignes, seulement des lignes entières ; ça vous parle ? Bon en tous cas je vais suivre vos conseils et me mettre à CodeBlocks asap.
 
Sinon, il semble effectivement que le pointeur "this" qui pointe sur l'objet courant soit la solution à mon problème ; en l'occurrence, pour ceux que ça intéresse, on a les deux possibilités équivalentes :

Code :
  1. double Truc::Norme()
  2. {
  3.     return( sqrt(   (*this).NormeAuCarre()   ) );
  4. }


ou  

Code :
  1. double Truc::Norme()
  2. {
  3.     return( sqrt(   this->NormeAuCarre()   ) );
  4. }


La voilà l'utilité de ce fameux pointeur ! (elle n'est pas évidente à première vue lorsqu'on lit un cours de C++)
 
Bon je risque fort d'avoir d'autres questions dans les jours prochains alors à très bientôt !

Reply

Marsh Posté le 11-02-2009 à 07:31:05    

sauf que non, tu n'en as pas besoin normalement si l'appel est non-ambigu ....

Reply

Marsh Posté le 11-02-2009 à 17:47:58    

Ecoute vu que j'ai corrigé pas mal de bugs entre temps, peut-être qu'en fait ce n'était pas ça le problème. Je toujours essayer d'enlever le this pour voir si ça fonctionne encore ?
 
Autre question, du même niveau : j'ai une classe Point par exemple, et une classe Cercle qui a comme attributs :

Code :
  1. private:
  2.     double rayon;
  3.     Point centre;


Comment écrire le constructeur par défaut de Cercle, sachant que je veux qu'il appelle le constructeur par défaut de la classe Point ? Est-ce que c'est

Code :
  1. Cercle::Cercle()
  2. {
  3.     rayon = 1;
  4. }


? ou est-ce plus subtil ?
 
Merci d'avance.

Reply

Marsh Posté le 11-02-2009 à 17:56:06    

Si tu veux

Code :
  1. Cercle::Cercle()
  2.    : rayon(1)
  3. {}


suffit.  Mais tu peux etre explicite avec

Code :
  1. Cercle::Cercle()
  2.    : rayon(1), centre()
  3. {}

Reply

Marsh Posté le 11-02-2009 à 17:59:10    

ca suffit oui

Reply

Marsh Posté le 11-02-2009 à 18:03:36    

Super merci beaucoup à vous deux ! C'est mieux d'utiliser une liste d'initialisation donc ?
 
Et si maintenant je veux surcharger le constructeur pour mettre un point en paramètre je fais :

Code :
  1. Cercle::Cercle(Point pt)
  2.     : rayon(1), centre(pt)
  3. {}


vrai ?

Reply

Marsh Posté le 11-02-2009 à 18:04:37    

oui sauf que Point serait à passer en reference constante :
 

Code :
  1. Cercle::Cercle(Point const& pt)   : rayon(1), centre(pt)
  2. {}


 
pr eviter des copies inutiles

Reply

Marsh Posté le 11-02-2009 à 18:09:45    

OK, et donc aussi dans mon prototype je suppose ?

Code :
  1. public:
  2.     Cercle(Point const& pt);


Et si ensuite j'ai une méthode de Cercle qui modifie le centré (via des méthodes Point::set_x(double nouveau_x) ) ce n'est pas gênant d'avoir mis un const ?

Reply

Marsh Posté le 11-02-2009 à 18:14:22    

..et pour l'appel du coup je fais

Code :
  1. Cercle circle(pt);


ou bien

Code :
  1. Cercle circle(&pt);


ou encore autre chose ?
 
Désolé de vous embêter hein :)

Reply

Marsh Posté le 11-02-2009 à 19:07:58    

Non, tu passes jsute tona rguemnt de manière constante, puis tu en fais une copie. La copie elle n'est pas constante.
 
Pour l'appelle Cercle c(pt) suffit, car référence != pointeur

Reply

Marsh Posté le 11-02-2009 à 19:23:48    

C'est bien ça le problème. J'avais bien compris (enfin je crois) les pointeurs, mais les références ça m'embrouille tout dans mes petits neurones. Si je comprends bien :
- le passage par référence sert à éviter une copie inutile de l'argument ;
- mais du coup cet argument risquerait d'être modifié, donc on met const pour l'empêcher ?
 
Pour résumer, je mets :
- dans le prototype :

Code :
  1. Cercle(Point pt);


- dans la définition :

Code :
  1. Cercle::Cercle(Point const& pt)


- dans l'appel :

Code :
  1. Cercle circle(pt);


Right ?

Reply

Marsh Posté le 11-02-2009 à 19:37:19    

Presque :
 
- dans le prototype :

Code :
  1. Cercle(Point const& pt);


- dans la définition :

Code :
  1. Cercle::Cercle(Point const& pt)


- dans l'appel :

Code :
  1. Cercle circle(pt);


Reply

Marsh Posté le 11-02-2009 à 19:49:10    

En effet ! D'ailleurs mon compilo n'a pas manqué de m'insulter quand j'ai essayé d'enlever le const& dans le prototype :) Et puis entre-temps j'ai relu la petite fiche que je m'étais faite sur les références, ça commence à se ré-éclaircir.
 
Merci encore en tous cas pour toute ton aide, tu me fais gagner un temps précieux ! A très bientôt pour de nouvelles questions (ce soir sans doute)

Reply

Marsh Posté le 12-02-2009 à 16:01:29    

Comme promis je reviens avec deux nouvelles questions. Je précise que j'ai déjà trouvé des solutions via des recherches sur le net mais je ne suis pas sûr qu'elles soient optimales.
 
J'explique mon problème ; j'ai un objet de ma classe Cercle (et plus tard d'autres classes géométriques) et je voudrais connaître quelles sont les valeurs possibles pour l'abscisse d'un point de mon cercle. La réponse est un intervalle, donc j'ai créé un classe intervalle dont voici la déclaration (je vous laisse deviner l'implémentation) :

Code :
  1. class Intervalle
  2. {
  3.     public:
  4.         Intervalle();
  5.         double getcentre() const;
  6.         double getrayon() const;
  7.         void setcentre(double c);
  8.         void setrayon(double r);
  9.         std::string afficher() const;
  10.     private:
  11.         double centre;
  12.         double rayon;
  13. }


La méthode "afficher" renvoie une chaîne de caractères "[ a ; b ]" où a=centre-rayon et b=centre+rayon sont les bornes de l'intervalle. Premier problème : la concaténation des string avec l'opérateur + ne marche pas avec des doubles, c'est-à-dire qu'en écrivant :  

Code :
  1. return("[ " + centre - rayon + " ; " + centre + rayon + " ]" );


je me fais insulter par le compilo. Donc il faut convertir les double en string --> solution trouvée sur un forum anglophone, utiliser un flux, donc un #include<sstream> et dans ma méthode :

Code :
  1. string Intervalle::afficher() const
  2. {
  3.     ostringstream f1,f2;
  4.     string ch1,ch2;
  5.     f1 << centre - rayon;
  6.     f2 << centre + rayon;
  7.     ch1 = f1.str();
  8.     ch2 = f2.str();
  9.     return("[ " + ch1 + " ; " + ch2 + " ]" );
  10. }


Mais ça me paraît un peu compliqué, auriez-vous plus simple ?
 
Deuxième problème : dans la classe Cercle je veux créer ma méthode qui renvoie l'Intervalle des abscisses, et là problème.. je ne sais pas comment mettre un objet comme valeur de retour ! Enfin dans le prototype OK c'est facile je fais :

Code :
  1. Intervalle abscisses() const;


mais dans l'implémentation je ne sais pas quoi mettre en return... C'est débile non ? Entre temps j'ai trouvé une solution grâce à mes réivsions sur les références (merci Joel F) : passer un Intervalle par référence, donc ma fonction devient :

Code :
  1. void abscisses(Intervalle &I) const;


et dans la fonction je fais du

Code :
  1. I.setrayon(rayon);

etc. Mais quelle est la bonne méthode ?
 
Merci !

Reply

Marsh Posté le 12-02-2009 à 18:05:14    

Q1/ non c'est la bonne manière.
Q2/ t'as le choix :
- soit ut fais untruc avec une reference pr la sortie et tu fais comemù tu dis
- soit tu construit un objet à renvoyer :
 

Code :
  1. Intervalle abcisses() const
  2. {
  3.   Intervalle r;
  4.   r.setRayon()
  5.   r.setCentre();
  6.   return r; 
  7. }


 
Par contre ça implique que Intervalle satisfasse la Forme Canonique de Coplien (je te laisse te renseigner la dessu). De même je pense qu'il serait plus judicieux que intervalle ets un constructeur qui prenne cercle et rayon et qui l'initialise.
Tu pourrais alors faire :
 

Code :
  1. Intervalle abscisses() const
  2. {
  3.   return Intervalle(*this,r);
  4. }

Reply

Marsh Posté le 13-02-2009 à 01:40:50    

Merci pour ta réponse. J'ai fait une petite recherche sur la forme canonique de Coplien, je vois à peu près ce que c'est (justement je suis en plein dans la surcharge des opérateurs).  
 
Pas de problème pour fournir systématiquement le constructeur par copie et l'opérateur = s'il le faut, mais pour le destructeur j'ai un petit doute ; pour des objets simples comme les miens, il n'y a rien à implémenter non ? Il n'y a pas d'allocation dynamique ni de pointeurs... Du coup dans Intervalle.cpp j'écris juste, dis-moi si je délire :

Code :
  1. Intervalle::~Intervalle() { }


 
En effet tu as raison il est plus simple de faire directement un constructeur surchargé plutôt que d'utiliser les accesseurs.  
 
Merci pour la confirmation pour ma Q1, du coup on peut faire un point plus simple :

Code :
  1. string Intervalle::afficher() const
  2. {
  3.     ostringstream F;
  4.     F << "[ " << m_centre - m_rayon << " ; " << m_centre + m_rayon << " ]";
  5.     return(F.str());
  6. }

Reply

Marsh Posté le 15-02-2009 à 19:14:07    

Salut tout le monde,
 
Alors pour commencer je uppe ma question sur le destructeur, dans le cas de classes très simples.
 
Ensuite voilà une nouvelle question : j'ai une classe Point, avec deux attributs double m_x et double m_y. J'ai surchargé l'opérateur d'affectation, et les opérateurs +,-,* en écrivant :

Code :
  1. Point Point::operator+(const Point &point)
  2. {
  3.     Point resultat(m_x + point.m_x, m_y + point.m_y);
  4.     return resultat;
  5. }


idem pour le -, pour le * c'est le produit scalaire :

Code :
  1. double Point::operator*(const Point &point)
  2. {
  3.     return(m_x * point.m_x + m_y * point.m_y);
  4. }


et il y aussi la multiplication par un double :

Code :
  1. Point Point::operator*(double s)
  2. {
  3.     Point resultat(m_x * s, m_y * s);
  4.     return resultat;
  5. }


Jusqu'ici tout va bien.
 
Ensuite j'ai une classe Triangle, qui a trois attributs : Point m_A, Point m_B, Point m_C. Je veux écrire une méthode barycentre, qui donne simplement (m_A + m_B + m_C) / 3, donc j'écris :

Code :
  1. Point Triangle::barycentre() const
  2. {
  3.     double s = 1 / 3.0; 
  4.     Point resultat;
  5.     resultat = ((m_A + m_B) + m_C) * s;
  6.     return resultat;
  7. }


Et là big problem : le compilateur m'envoie  
"In member function 'Point Triangle::barycentre() const':
passing 'const Point' as 'this' argument of 'Point Point::operator+(const Point& )' discards qualifier"
Kézako ? J'ai trouvé une solution de rechange en écrivant

Code :
  1. {
  2.     double s = 1 / 3.0; 
  3.     Point resultat((m_A.getx() + m_B.getx() + m_C.getx()) * s, (m_A.gety() + m_B.gety() + m_C.gety()) * s);
  4.     return resultat;
  5. }


(en utilisant le constructeur surchargé Point(double x, double y) et les accesseurs) mais je trouve ça dommage de ne pas pouvoir utiliser mes opérateurs surchargés...
 
Merci d'avance pour vos éclaircissements !

Reply

Marsh Posté le 15-02-2009 à 20:30:32    

les operateurs symmétriques comme + doivent etre implanté comme fonction libre non-friend et non comme membre. Mieux, la fonction + doit etre implanté à partir de la méthode +=

 
Code :
  1. class Point
  2. {
  3. //....
  4.   Point& operator +=( Point const& src )
  5.   {
  6.      x += src.getx();
  7.      y += src.gety();
  8.      return *this;
  9.   }
  10. };
  11. Point operator+( Point const& l, Point const& r )
  12. {
  13.   Point res(l);
  14.   res += r;
  15.   return r;
  16. }


Message édité par Joel F le 15-02-2009 à 20:32:43
Reply

Marsh Posté le 15-02-2009 à 21:24:13    

Ah ah alors ça je ne l'aurais pas trouvé tout seul. C'est cool ! Et ça me plait plus une définition symétrique comme ça. J'en apprends tous les jours avec toi mon cher Joel F.  
 
Et pour la multiplication (Point * double) je mets une fonction "hors-classe" aussi ?

Reply

Marsh Posté le 15-02-2009 à 22:00:21    

Autre question : pourquoi le type de retour de la méthode est-il Point& et pas Point ? (*this) désigne bien l'objet lui-même non ?

Reply

Marsh Posté le 15-02-2009 à 22:26:29    

en general, les fonctions symmetriques sont externes oui.
Pour +=, bah += c'ets + et = et = renvoit this pr pouvoir chainer :
 
a= p += q+r;

Reply

Marsh Posté le 15-02-2009 à 22:43:57    

OK je note. Cela dit Point * double n'est pas symétrique (et j'en ai d'autres comme ça, Matrice * double, Matrice * point...)
 
En fait j'ai du mal à comprendre le coup du Point&.  
 
Perso, puisque += écrase les anciennes valeurs des attributs, intuitivement j'aurais défini ça comme une méthode void :

Code :
  1. void Point::operator+=(Point const& Pt)
  2. {
  3.     m_x += Pt.m_x;
  4.     m_y += Pt.m_y;
  5. }


Non ? Et d'ailleurs est-ce qu'il vaut mieux utiliser les méthodes get pour accéder aux attributs de l'autre objet, même si on reste dans la classe ?

Reply

Marsh Posté le 15-02-2009 à 22:49:56    

non, toutes les variantes de = doivent renvoyer l'objet modifier pr le chainage :
a = b =c;
ou a += b += d;
 
pr Point*double, tu oublies que double*Point est semantiquement identique, elle ets donc symetrique.

Reply

Marsh Posté le 15-02-2009 à 22:59:38    

OK je comprends mieux. Mais (désolé d'être lourd) le type de l'objet est Point tout court ; je ne comprends pas pourquoi le type de retour de la fonction est Point& ? On renvoie le point et pas une référence sur le point...
 
D'accord pour la symétrie, pour moi ça ne coulait pas de source car je voyais ça comme une méthode

Code :
  1. Point Point::operator*(double s)


du coup le point est forcément le premier opérande et on ne peut pas écrire double * Point. Alors que si on le met en fonction libre on peut définir les deux fonctions :

Code :
  1. Point operator*(double s, Point const& p)


Code :
  1. Point operator*(Point const& p, double s)

Reply

Marsh Posté le 15-02-2009 à 23:04:53    

bah si = ou += renvoyait une instance, il faudrait la recopier depuis la pile vers sa destination, ce qui serait bien couteux.

Reply

Marsh Posté le 15-02-2009 à 23:17:40    

Ouaip je vois à peu près... Bon il faut que je médite tout ça.
 
Je commence par modifier mon code suivant tes conseils et je te raconte ce que ça donne.

Reply

Marsh Posté le 15-02-2009 à 23:44:07    

Par contre l'opérateur d'affectation = je le laisse comme membre de la classe (because forme canonique de Coplien etc.) ou alors non (because égalités chaînées) ?

Reply

Marsh Posté le 15-02-2009 à 23:52:59    

En fait l'histoire de renvoyer une référence plutôt qu'une instance ça vaut pour n'importe quelle fonction renvoyant un objet non ?
 
Après promis j'arrête de te submerger de questions.

Reply

Marsh Posté le 16-02-2009 à 07:28:41    

operator= n'est pas symetrique car le membre gauche à un role particulier :=> membre
 
Pour le retour de référence, ca ne vaut que si tu veut chainer tes appels sur un meme objet initial. Sinon tu renvois une instance vu que tu n'as pas le droit de renvoyer une reference sur un objet temporaire.

Reply

Marsh Posté le 18-02-2009 à 00:36:01    

Yo,
 
Bon ça marche très bien en mettant les =,+=, etc."in-class" et led +,-,*, "out-class". Et en fait renvoyer un Objet ou un Objet& ne change pas trop le fonctionnement (du mons pour l'usage que j'en fais, pas d'affectations en chaîne).

Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed