Partage
  • Partager sur Facebook
  • Partager sur Twitter

Auteur const& getAuteur() {return auteur_;}

Sujet résolu
18 août 2018 à 23:38:27

Bonsoir la communauté,

Je n'arrive pas à bien comprendre un truc avec les références. J'explique rapidement le code du coup :

class Auteur {
private:
    string nom_;
...}

J'ai une classe Auteur avec plein de bazar dedans, et une classe Oeuvre.

class Oeuvre {
private:
    string titre_;
    Auteur const& auteur_;

L'auteur de la classe Oeuvre peut donc être initialisé par le constructeur mais pas modifié par la suite, de plus la référence évite de faire un copie. Non? 

public:
    Oeuvre(string titre, Auteur const& auteur, string langue)
        :titre_(titre), auteur_(auteur), langue_(langue)
    {}

    Auteur const& getAuteur() {return auteur_;}


Si je ne dis pas de bêtise ici le return auteur_ va me retourner l'adresse de celui-ci. MAIS je n'en suis pas sûr et du coup je voulais savoir avec un cout ce qu'il me retournait.

Cependant quand j'essaye de faire un cout de cette fonction dans le main il me sort cette erreur.

invalid operands to binary expression ('std::ostream' (aka 'basic_ostream<char>') and 'const Auteur')

Et le pire (vu que je ne comprends pas non plus! :) ) ce que lorsque j'essaye de mettre le cout dans le getter comme cela :

Auteur const& getAuteur() {cout << &auteur_; return auteur_;}

il n'accepte rien d'autre à part la référence avec le &.

Quelqu'un pourrait m'expliquer ces différents comportement svp? :)


Bonne soirée!



-
Edité par Irtis 18 août 2018 à 23:53:07

  • Partager sur Facebook
  • Partager sur Twitter
18 août 2018 à 23:58:48

1. il faut arreter de penser "reference = adresse". Ce qui se passe est plus compliquée (donc pas toujours vrai) et inutile de penser comme ca.

2. tu essaies de passer un objet Auteur a un std::cout ? Comment std::cout pourrait savoir quoi faire de Auteur ?

  • Partager sur Facebook
  • Partager sur Twitter
19 août 2018 à 0:32:22

Salut,

une référence, c'est un moyen de représenter une donnée en créant un lien de référencement au moyen d'un identificateur. Exemple :

void sqrt_ref(int &value) {
    value *= value; // ici, value est " l'alias " de val (dans foo)
}

void foo() {
    int val = 9;
    sqrt_ref(val);
    // val = 81
}

Donc, à chaque fois que tu utilises les références, tu modifies en fait l'objet référé.

Irtis a écrit:

Auteur const& getAuteur() { return auteur_; }

Si je ne dis pas de bêtise ici le return auteur_ va me retourner l'adresse de celui-ci. MAIS je n'en suis pas sûr et du coup je voulais savoir avec un cout ce qu'il me retournait.

Une fonction-référence, elle, permet de faire une sorte de pont entre un objet via une référence constante (= pas d'objets temporaires à ce niveau-là). Par exemple :

int a;

int &GetRef_a() {
    return a;
}

GetRef_a() = 9;

Nous déclarons l'objet a, nous créons une fonction permettant de le référencer, puis nous utilisons cette fonction comme intermédiaire entre l'objet a et la valeur que nous lui assignons. Plusieurs réponses avaient étés donnée ici à ce propos.

Ce type de fonctions (et les références en générales) ne sont pas identiques aux pointeurs, donc une référence n'est pas une simple abstraction de pointeur qui renvoie simplement une adresse ou autre dans tous les cas.

Mais, en comparaison, c'est presque la même chose que si nous faisions ça (mais pas égale !) :

int a;

int *GetPtr() {
    return &a;
}

int *a_ptr = GetPtr();
*a_ptr = 9;

Dans ton exemple, 

Irtis a écrit:

Auteur const& getAuteur() {cout << &auteur_; return auteur_;}

C'est simplement que auteur_ est une classe, et ton message d'erreur l'indique bien : il n'accepte que les fluxes littéraux.

-
Edité par vanaur 19 août 2018 à 0:34:19

  • Partager sur Facebook
  • Partager sur Twitter

Le meilleur moyen de prédire l'avenir, c'est de l'inventer | N'oubliez pas [résolu] et +1 | Excusez mon ôrtograffe, j'essaie de l'améliorer...

19 août 2018 à 11:51:52

Effectivement c'est complètement bête d'utiliser directement la class dans le cout. J'ai un peu de mal à utiliser les class dans les class pour l'instant. Et pour les références je comprends un peu mieux now. :)

Du coup j'ai changé mon code:

class Auteur {
private:
    string nom_;
...   
public:
     string getNom() const {return nom_;}
...
};

J'ai du coup un getNom const que je vais utiliser dans la class Oeuvre.

class Oeuvre {
private:
    string titre_;
    const Auteur& auteur_;
    string langue_;

public:
    /* constructeur */
    Oeuvre(string titre, const Auteur& auteur, string langue)
        :titre_(titre), auteur_(auteur), langue_(langue)
    {
    }
...

    /* methodes */
    void affiche(){
        cout << titre_ << ", " << auteur_.getNom() << ", en " << langue_ << endl;
    }

};

Ce que je veux faire c'est :

  • Utiliser la classe Auteur par référence (pour éviter un copie)
  • La déclarer en const (dans la class  Oeuvre) pour éviter qu'elle ne soit modifiée (Cependant est-ce utile ici vu que mes attribus sont en private: dans la classe Auteur? Mais aussi parce que mon auteur.getNom() est lui aussi déclaré en const?) 
  • retourner l'ensemble des infos via une méthode avec un cout dans la Class Oeuvre

Est-ce que mon code correspond bien à cette démarche? Ou est-ce qu'il y a des choses à améliorer/incohérentes?

Merci pour votre aide!

EDIT :

Là où je veux en venir aussi c'est à l'utilisation de const, car :

Auteur const&

fait exactement la même chose(Ou en tout cas dans mon code) que :

const Auteur&

Est-ce qu'il y a une subtilité?


-
Edité par Irtis 19 août 2018 à 11:56:23

  • Partager sur Facebook
  • Partager sur Twitter
19 août 2018 à 12:21:58

Irtis a écrit:

Est-ce qu'il y a une subtilité?

Non. C'est juste 2 écritures pour la même chose. Historiquement, on utilise beaucoup "const Auteur&" (cela utilise une regle speciale qui dit que s'il n'y a rien a gauche du const, alors le const s'applique a droite). Mais "on" a de plus en plus tendance a penser que c'est mieux d'ecrire "Auteur const&".

Irtis a écrit:

Est-ce que mon code correspond bien à cette démarche? Ou est-ce qu'il y a des choses à améliorer/incohérentes?

- Pour les const, si Auteur n'a pas besoin de modifier "nom", alors il faut effectivement le mettre en const.

- En général, on n'aime pas les getters. (Cf encapsulation, Demeter, etc). Ici, pourquoi une fonction "afficher" dans Oeuvre et un getter dans Auteur ? La cohérence permet de faciliter la lecture du code et de gagner du temps a l’écriture.

- pourquoi "afficher" et pas un "operator<<" ? (généricité)

- on va laisser de cote les semantiques d'entite et valeur pour le moment. Mais il faudra s'y interesser un jour.

- le gros probleme... on n'utilise pas souvent des references comme membre :(

Ton avis sur ce qui se passe dans ce code :

Auteur ecrivain("toto");
Oeuvre livre("Le C++ pour debutants", ecrivain, "fr");

// et on a plusieurs auteurs...
std::vector<Auteur> auteurs;
auteurs.push_back(ecrivain); // ???

// et on a plusieurs oeuvres...
std::vector<Oeuvre> bibliotheque;
bibliotheque.push_back(livre); // ???



  • Partager sur Facebook
  • Partager sur Twitter
19 août 2018 à 12:26:29

Irtis a écrit:

const Auteur&
Auteur const&

Est-ce qu'il y a une subtilité?

Oui et non; pour nous (développeurs C++), ça ne change rien. C'est juste que la norme C++ actuelle définit le mot-clef const comme affectant "normalement" la valeur qui se trouve à sa gauche, si le compilateur n'en trouve aucune, il affecte la valeur à sa droite. C'est tout.

Irtis a écrit:

Utiliser la classe Auteur par référence (pour éviter un copie)

Utiliser les références pour éviter les copies, c'est un peu stupide pour de petites données je trouve (peut même être dangereux au sein d'une classe en C++, car il ne possède pas de garbage collector pour assurer l’existence d'un objet, tu pourrais -- si tu ne fais pas attention -- te retrouver avec des objets supprimés auxquels tu tentes d'accéder par référence en dehors de la classe).

Irtis a écrit:

La déclarer en const (dans la class  Oeuvre) pour éviter qu'elle ne soit modifiée (Cependant est-ce utile ici vu que mes attribus sont en private: dans la classe Auteur? Mais aussi parce que mon auteur.getNom() est lui aussi déclaré en const?) 

Utilise const dès que tu le peux, cela évite d'avoir de mauvaises surprises. Que ce soit en private ou en public, l'accès aux données est juste intermédiaire.

Irtis a écrit:

retourner l'ensemble des infos via une méthode avec un cout dans la Class Oeuvre

Le vrai terme est "renvoyé" ^^ . D'ailleurs on ne renvoie rien d'une fonction void.

std::cout est une fonction d'IO qui permet l'affichage dans la console, tu ne peux rien renvoyée avec std::cout, juste afficher.

Pour le reste, gbdivers à déjà répondu ;)

-
Edité par vanaur 19 août 2018 à 12:28:15

  • Partager sur Facebook
  • Partager sur Twitter

Le meilleur moyen de prédire l'avenir, c'est de l'inventer | N'oubliez pas [résolu] et +1 | Excusez mon ôrtograffe, j'essaie de l'améliorer...

19 août 2018 à 12:39:37

vanaur a écrit:

std::cout est une fonction d'IO qui permet l'affichage dans la console, tu ne peux rien renvoyée avec std::cout, juste afficher.

(Trolllll : std::cout n'est pas une fonction. Et le code suivant est valide... ;) )

auto& x = std::cout << "hello";




  • Partager sur Facebook
  • Partager sur Twitter
19 août 2018 à 12:59:38

gbdivers a écrit:

- En général, on n'aime pas les getters. (Cf encapsulation, Demeter, etc). Ici, pourquoi une fonction "afficher" dans Oeuvre et un getter dans Auteur ? La cohérence permet de faciliter la lecture du code et de gagner du temps a l’écriture.

Si je comprends ce que tu me dit il faut éviter de faire des getters(/setters aussi par la même occasion je suppose). Du coup comment j'accède à la valeur nom de l'auteur. Est-ce que je fais une méthodes afficheNom dans Auteur?

gbdivers a écrit:

- pourquoi "afficher" et pas un "operator<<" ? (généricité)

- on va laisser de cote les semantiques d'entite et valeur pour le moment. Mais il faudra s'y interesser un jour.

Je n'ai pas compris le operator. (Je n'ai pas encore fini le cours que je fais sur l'orienté objet, du coup il doit me manquer des trucs encore!)

Je vais regarder pour la sémantique (j'ai trouvé un site sympa http://guillaume.belz.free.fr/doku.php?id=semantique_d_entite ;) hé hé )

gbdivers a écrit:

- le gros probleme... on n'utilise pas souvent des references comme membre :(

Ton avis sur ce qui se passe dans ce code :

Auteur ecrivain("toto");
Oeuvre livre("Le C++ pour debutants", ecrivain, "fr");

// et on a plusieurs auteurs...
std::vector<Auteur> auteurs;
auteurs.push_back(ecrivain); // ???

// et on a plusieurs oeuvres...
std::vector<Oeuvre> bibliotheque;
bibliotheque.push_back(livre); // ???

 Le mot "membre" veut dire quoi? Class dans une class?

Pour 

auteurs.push_back(ecrivain); // ??? 

Je suppose qu'il va simplement rajouté en tant que copie ecrivain dans le tableau

bibliotheque.push_back(livre);

Il fait une copie de copie pour auteur? :D

EDIT : Mais si on utilise les références, 

auteurs.push_back(ecrivain); // ??? 

va avoir la référence d'écrivain

bibliotheque.push_back(livre);

Mais ici je ne sais pas trop en fait

Merci Vanaur aussi pour ta réponse!



-
Edité par Irtis 19 août 2018 à 13:04:09

  • Partager sur Facebook
  • Partager sur Twitter
19 août 2018 à 13:06:26

gbdivers a écrit:

(Trolllll : std::cout n'est pas une fonction. Et le code suivant est valide... ;) )

auto& x = std::cout << "hello";

Petite coquille ^^ : std::cout est un objet de gestion de flux (avec tous les xxstream d'iostream).

-
Edité par vanaur 19 août 2018 à 13:06:41

  • Partager sur Facebook
  • Partager sur Twitter

Le meilleur moyen de prédire l'avenir, c'est de l'inventer | N'oubliez pas [résolu] et +1 | Excusez mon ôrtograffe, j'essaie de l'améliorer...

19 août 2018 à 13:22:24

Irtis a écrit:

Si je comprends ce que tu me dit il faut éviter de faire des getters(/setters aussi par la même occasion je suppose). Du coup comment j'accède à la valeur nom de l'auteur. Est-ce que je fais une méthodes afficheNom dans Auteur?

Imagines si finalement une chaine de caracteres n'est pas suffisant pour gerer le nom et que tu crees ensuite une classe Nom :

struct Nom {
    string nomDeFamille;
    string prenom;
    vector<string> autresNoms;
    vector<string> pseudonymes;
}

Que devient ta fonction "getName" ? Ca la rend obsolete ? Il faut reecrire tous les codes qui utilisent cette fonction ?

Alors que si tu n'exposes pas ce "detail interne a la classe", par exemple en ecrivant une fonction "afficher" plutot que "getName", alors si tu modifie "m_nom", tu ne dois pas changer les codes qui utilisent "afficher".

C'est l'idee du principe de Demeter : ne pas exposer les details internes. Ce qui permet, lors qu'on modifie le code interne d'une classe, de ne pas devoir changer le code qui utilise cette classe. Donc avoir un code beaucoup plus simple a maintenir.

(En pratique, on le fait quand même de temps en temps. Mais a chaque fois que l'on fait un changement dans une classe et qu'on doit se taper des dizaines ou centaines d'erreurs de compilation a cause de ca, ca casse bien les pieds)

Irtis a écrit:

Je n'ai pas compris le operator. (Je n'ai pas encore fini le cours que je fais sur l'orienté objet, du coup il doit me manquer des trucs encore!)

Ok, tu lis le cours C++ du site. Je te laisse rechercher ce que les gens pensent de ce cours...

En fait, c'est une erreur d'ecrire une fonction "afficher".

Quand tu ecris :

std::string const hello { "hello, world" };
std::cout << hello << std::endl;

En fait, cela appelle une fonction particuliere qui s'appelle "operator<<" et qui prend en parametre un std::string.

Le gros avantage de cette approche, c'est qu'un tel operateur pourra etre appelle sur n'importe quel flux (stream) de sortie : afficher a l'ecran, afficher une erreur, enregistrer dans un fichier, voire ecrire dans un JSON, ecrire sur le reseau, etc. Voire ecrire tes propres flux de donnees.

Il n'y a pas trop de raison de proposer une fonction non generique comme "afficher" plutot qu'une fonction plus generique comme "operator<<".

Irtis a écrit:

Le mot "membre" veut dire quoi? Class dans une class?

Pour auteurs.push_back(ecrivain); // ??? 

Je suppose qu'il va simplement rajouté en tant que copie ecrivain dans le tableau

Par conte pour bibliotheque.push_back(livre);

Il fait une copie de copie pour auteur? :D

Oui, un "membre", c'est "quelque chose dans une classe. (On parle de fonction membre, de variable membre/attribue, etc)

Pour le code, pour faire simple : ca crash le programme ! Le code est tout simplement invalide.

Mais on verra ca plus tard. Faut juste retenir pour le moment que tu ne peux pas faire ca.

  • Partager sur Facebook
  • Partager sur Twitter
19 août 2018 à 15:12:34

Merci pour ta réponse, la partie sur les getters est beaucoup plus claire maintenant!

Pour le operator<< je viens de regarder et c'est dans le chapitre suivant du cours que je suis! :)

Je fais le cours initiation à la POO sur coursea, c'est vraiment bien je trouve. Ils présentent les "nouveauté" c++ 11, avec les for auto, les smarts pointeurs et autres. Et effectivement j'avais fait par le passé le cours sur le site du zéro (à l'époque :) ) mais ça m'avait plus perturbé qu'autre chose... 

Ok pour le code, je mets ça dans un coin de ma tête et je reviendrai dessus plus tard.

  • Partager sur Facebook
  • Partager sur Twitter
19 août 2018 à 16:31:35

Irtis a écrit:

Ok pour le code, je mets ça dans un coin de ma tête et je reviendrai dessus plus tard.

Un point que je n'avais pas précisé : ce probleme de validité n'est pas spécifique aux références, mais pour TOUTES les indirections (itérateurs, pointeurs, références, etc). Cf http://guillaume.belz.free.fr/doku.php?id=references#validite_des_indirections pour ce probleme de validité. Pour résumer simplement : une indirection permet d’accéder a un objet, si l'objet est détruit ou déplace, l'indirection devient invalide. (Sutter a appelé ce probleme "the 30 years old problem"... pour te dire a quel point c'est un gros gros gros probleme en C++)

-
Edité par gbdivers 19 août 2018 à 16:33:22

  • Partager sur Facebook
  • Partager sur Twitter
20 août 2018 à 15:46:54

Merci! Est-ce que tu peux me préciser ce point stp?

gbdivers a écrit:

- le gros probleme... on n'utilise pas souvent des references comme membre :(

Ton avis sur ce qui se passe dans ce code :

Auteur ecrivain("toto");
Oeuvre livre("Le C++ pour debutants", ecrivain, "fr");

// et on a plusieurs auteurs...
std::vector<Auteur> auteurs;
auteurs.push_back(ecrivain); // ???

// et on a plusieurs oeuvres...
std::vector<Oeuvre> bibliotheque;
bibliotheque.push_back(livre); // ???

 Je ne suis pas sûr de comprendre où tu veux en venir.. J'ai compris le reste des éléments mais pas ça. :/



  • Partager sur Facebook
  • Partager sur Twitter
20 août 2018 à 16:00:52

Irtis a écrit:

Je ne suis pas sûr de comprendre où tu veux en venir.. J'ai compris le reste des éléments mais pas ça. :/

J'ai répondu dans mon dernier message :)

Pour résumer simplement : une indirection permet d’accéder a 
un objet, si l'objet est détruit ou déplace, l'indirection devient invalide. (Sutter a 
appelé ce probleme "the 30 years old problem"... pour te dire a quel point c'est un 
gros gros gros probleme en C++)

La reference vers un objet qui se trouve dans un vector sera invailde dès que l'objet est bougé. Par exemple si on fait un push_back ou ce genre de choses.

-
Edité par gbdivers 20 août 2018 à 16:01:24

  • Partager sur Facebook
  • Partager sur Twitter