Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Bizarre

6 réponses
Avatar
Etienne Rousee
Bonjour,

Est ce que quelqu'un a déjà eu ce type d'erreur de
compilation (avec VC++6.0):
(l'erreur ne se produit plus si j'enlève le "vector"
et le "using ...", mais j'en ai besoin après).

Fichier Test.h:
class Test
{
public:
friend ostream& toto(ostream &, const Test &);
friend ostream& operator<<(ostream&, const Test &);
private:
int _nb;
};

Fichier Test.cpp:
#include <iostream>
#include <vector>

using namespace std;

#include "Test.h"

ostream& toto(ostream &out, const Test &T)
{
T._nb; // Pas de problème, ça passe.
return out;
}

ostream& operator<<(ostream &out, const Test &T)
{
T._nb; // Erreur: tentative d'accès à des données privées.
return out;
}

int main()
{
return 0;
}

6 réponses

Avatar
Fabien LE LEZ
On Sat, 10 Apr 2004 20:06:44 +0200, "Etienne Rousee"
wrote:

Fichier Test.h:

class Test
{
public:
friend ostream& toto(ostream &, const Test &);
friend ostream& operator<<(ostream&, const Test &);


Bon, je sais que c'est courant comme méthode, mais j'aime pas :

- d'une part, les lignes "friend os..." ne déclarent pas de fonctions.
On a généralement moins d'erreurs bizarres en déclarant les fonctions
avant :

class Test;

ostream& toto(ostream &, const Test &);

class Test
{
public: ...



- d'autre part, ton .h n'est pas autosuffisant. En d'autres termes, un
.cpp ne contenant rien d'autre qu'un #include "test.h" ne compilerait
pas.
Il faudrait mettre le "#include <iostream>" et le "using namespace
std;" également dans le .h... sauf qu'on ne met pas de directive
"using" dans un .h.
Jusqu'ici, le .h devient :

--- début de test.h

#include <iostream> /* En théorie, je crois que cet #include ne suffit
pas, mais pour l'instant les compilos sont tellement en désaccord les
uns avec les autres que je préfère ne rien changer ici ;-) */

class Test;

std::ostream& toto (std::ostream &, const Test &);
std::ostream& operator<<(std::ostream&, const Test &);

class Test
{
public:
friend std::ostream& toto(std::ostream &, const Test &);
friend std::ostream& operator<<(std::ostream&, const Test &);
private:
int _nb;
};

--- fin de test.h


- Troisième point : je n'aime pas la technique "friend operator <<".
Je préfère largement la technique suivante (je mélange .h et .cpp pour
raccourcir mon post) :

class Test
{
public:
void Print (std::ostream &os) { os << _nb; }
private:
int _nb;
};

std::ostream << (std::ostream& os, Test const& t)
{
t.Print (os);
return os;
}

J'ai toutefois noté l'objection de James (était-ce bien lui ?) : en
gros, operator<< est sensé afficher l'état public d'un objet, et ne
devrait donc pas avoir besoin d'être friend, ni d'une fonction membre
du style "Print()". Si ta classe n'accepte aucun accès à "_nb", sa
valeur est uniquement la cuisine interne de la classe, et n'a pas à
être affiché (sauf à des fins de débogage).


PS : utiliser un identifiant commençant par "_" peut être dangereux,
même si rien n'est bien sûr en ce domaine. Cf
<http://www.google.com/advanced_group_search?hl=en> pour savoir
pourquoi, j'en ai marre de répéter tout le temps la même chose...


int main()
{
return 0;
}


Juste un truc en passant : j'ai remarqué (sous Borland C++) que si une
fonction ou une classe était déclarée mais jamais utilisée en
pratique, ça pouvait poser problème -- par exemple, une classe Truc
contenant un std::vector<Machin> fait planter la compilation si aucun
objet Truc n'est jamais instancié dans le code. C'est AMHA un bug du
compilo, mais parfois à prendre en compte quand on ne sait pas d'où
vient un bug ;-)

--
;-)
FLL, Epagneul Breton

Avatar
Etienne Rousee
"Fabien LE LEZ" a écrit ...
class Test;

ostream& operator<<(ostream &, const Test &);

class Test
{
public: ...


Merci, ceci a été suffisant.
Mais ça ne me dit pas pourquoi "operator<<" en a besoin
alors que "toto" s'en passe. C'est quand même curieux.

- Troisième point : je n'aime pas la technique "friend operator <<".


Et pourquoi donc ?

J'ai toutefois noté l'objection de James (était-ce bien lui ?) : en
gros, operator<< est sensé afficher l'état public d'un objet, et ne
devrait donc pas avoir besoin d'être friend, ni d'une fonction membre
du style "Print()". Si ta classe n'accepte aucun accès à "_nb", sa
valeur est uniquement la cuisine interne de la classe, et n'a pas à
être affiché (sauf à des fins de débogage).


Pas tout à fait. Je veux afficher un objet (mathématique en l'occurence)
tel qu'on l'affiche habituellement en math, sans que l'utilisateur de la
classe
n'ait accès à ma façon de l'implémenter, qui va probablement évoluer.
Je sais, j'aurais pu mettre des GetMachin() et des SetTruc()...

Etienne

Avatar
Loïc Joly
Etienne Rousee wrote:
- Troisième point : je n'aime pas la technique "friend operator <<".


Et pourquoi donc ?


Personellement, j'utilise la technique :
- Dans la class, une fonction virtuelle void print(ostream &os)
- Un opérateur qui ne fait qu'appeler cette fonction

Avantages :
On arrive aisi à simuler un "opérateur << virtuel"

Je sais, j'aurais pu mettre des GetMachin() et des SetTruc()...


Bof...

--
Loïc


Avatar
Loïc Joly
Etienne Rousee wrote:
- Troisième point : je n'aime pas la technique "friend operator <<".


Et pourquoi donc ?


Personellement, j'utilise la technique :
- Dans la class, une fonction virtuelle void print(ostream &os) const
- Un opérateur << qui ne fait qu'appeler cette fonction

Avantages :
On arrive aisi à simuler un "opérateur << virtuel"

Je sais, j'aurais pu mettre des GetMachin() et des SetTruc()...


Bof...

--
Loïc


Avatar
kanze
Fabien LE LEZ wrote in message
news:...
On Sat, 10 Apr 2004 20:06:44 +0200, "Etienne Rousee"
wrote:

Fichier Test.h:

class Test
{
public:
friend ostream& toto(ostream &, const Test &);
friend ostream& operator<<(ostream&, const Test &);


Bon, je sais que c'est courant comme méthode, mais j'aime pas :

- d'une part, les lignes "friend os..." ne déclarent pas de fonctions.


Alors, que doit signifier §11.4/3 dans la norme : « A function first
declared in a friend declaration has external linkage. » ?

On a généralement moins d'erreurs bizarres en déclarant les fonctions
avant :

class Test;

ostream& toto(ostream &, const Test &);


En général, je suis plutôt d'accord avec toi. Quand on a des références
croisées, du genre classe A réfère à classe B, et vice versa, je le
trouve aussi bon ton de declarer tout d'abord, et puis définir. En
revanche, je n'ai jamais eu de problèmes avec des fonctions friend.
Est-ce que tu es en train de me dire qu'il y a encore des surprises qui
m'attendent ici aussi ?

class Test
{
public: ...

- d'autre part, ton .h n'est pas autosuffisant. En d'autres termes, un
.cpp ne contenant rien d'autre qu'un #include "test.h" ne compilerait
pas.

Il faudrait mettre le "#include <iostream>" et le "using namespace
std;" également dans le .h... sauf qu'on ne met pas de directive
"using" dans un .h.


Et qu'il n'utilise rien qui n'est défini dans <iostream>, qui est
complètement superflu ici. C'est soit <iostream.h> (s'il doit supporter
d'anciens compilateurs), soit <iosfwd> (qui est préférable si on peut).

Jusqu'ici, le .h devient :

--- début de test.h

#include <iostream> /* En théorie, je crois que cet #include ne suffit
pas, mais pour l'instant les compilos sont tellement en désaccord les
uns avec les autres que je préfère ne rien changer ici ;-) */


Comme j'ai dit, non seulement non suffisant, mais pas nécessaire.
L'en-tête <iostream> ne déclare que les objets prédéfinis, du genre
std::cin et std::cout. Il ne définit rien.

class Test;

std::ostream& toto (std::ostream &, const Test &);
std::ostream& operator<<(std::ostream&, const Test &);

class Test
{
public:
friend std::ostream& toto(std::ostream &, const Test &);
friend std::ostream& operator<<(std::ostream&, const Test &);
private:
int _nb;
};

--- fin de test.h

- Troisième point : je n'aime pas la technique "friend operator <<".
Je préfère largement la technique suivante (je mélange .h et .cpp pour
raccourcir mon post) :

class Test
{
public:
void Print (std::ostream &os) { os << _nb; }
private:
int _nb;
};

std::ostream << (std::ostream& os, Test const& t)
{
t.Print (os);
return os;
}


Ce n'est vraiment intéressant que si tu conçois Test pour être une
classe de base, et que print est virtuelle. Au moins que, comme moi, tu
as une règle générale qui dit que toute fonctionnalité est implémentée
d'abord dans une fonction nommée, et que les opérateurs surchargés ne
sont jamais que des fonctions de renvoi. (Mais je ne sais plus ni d'où
ni pourquoi j'ai cette règle. C'est devenu une habitude, que je
continue, mais sans que je sache réelement pourquoi.)

J'ai toutefois noté l'objection de James (était-ce bien lui ?)


C'est de moi, mais c'est toutefois plutôt une considération qu'une
objection. C'est quelque chose dont je crois qu'il vaut la peine d'y
penser, mais ce n'est pas une règle absolue.

: en gros, operator<< est sensé afficher l'état public d'un objet, et
ne devrait donc pas avoir besoin d'être friend, ni d'une fonction
membre du style "Print()". Si ta classe n'accepte aucun accès à "_nb",
sa valeur est uniquement la cuisine interne de la classe, et n'a pas à
être affiché (sauf à des fins de débogage).


C'est en effet la question qu'il faut poser. Si _nb n'est qu'un détail
de l'implémentation, pourquoi l'afficher ? En revanche, si il fait bien
partie de l'état visible de l'objet (directement ou indirectement),
c'est un peu drôle qu'on ne peut pas le lire autrement qu'en
l'affichant.

--
James Kanze GABI Software mailto:
Conseils en informatique orientée objet/ http://www.gabi-soft.fr
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
Fabien LE LEZ
On 13 Apr 2004 05:34:27 -0700, wrote:

En
revanche, je n'ai jamais eu de problèmes avec des fonctions friend.
Est-ce que tu es en train de me dire qu'il y a encore des surprises qui
m'attendent ici aussi ?


Sais pas. Sur ce point, je ne fais que "recopier" ce que j'ai lu ici
même. Je n'ai moi non plus jamais eu de problèmes avec les fonctions
friend, car je les utilise très très peu...

--
;-)
FLL, Epagneul Breton