Pourquoi est il plus propre d'utiliser MaClasse::methode() dans un prototype de MaClasse.class,
plutôt que this->methode ?
ps : ca marche aussi avec les arguments
Version imprimable
Pourquoi est il plus propre d'utiliser MaClasse::methode() dans un prototype de MaClasse.class,
plutôt que this->methode ?
ps : ca marche aussi avec les arguments
Essaye ça et tu devrais avoir une piste pour comprendre:
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 #include <iostream> class A { public: virtual void MaMethode() { std::cout<<"A::MaMethode"<<std::endl; } virtual void MaSecondeMethode() { std::cout<<"A::MaSecondeMethode"<<std::endl; } void MaisPourquoi() { std::cout<<"this->MaMethode()"<<std::endl; this->MaMethode(); std::cout<<"A::MaSecondeMethode()"<<std::endl; A::MaSecondeMethode(); } }; class B :public A { public: virtual void MaMethode() { std::cout<<"B::MaMethode"<<std::endl; } virtual void MaSecondeMethode() { std::cout<<"B::MaSecondeMethode"<<std::endl; } }; int main(int argc, char* argv[]) { B b; b.MaisPourquoi(); return 0; }
Ca a donc un sens dans l'héritage.
Uniquement?
A priori je dirai que oui.
Et je ne dirai pas que l'un est plus propre que l'autre.
Ça dépend du comportement que tu veux. Si tu veux appeler la méthode du type réel de ta class (B), ou d'un de ces ancêtre spécifiquement.
Et quelle est la différence entre ces 2 versions ?J'ai l'impression que c'est juste une perte de lisibilité.Code:
1
2 this->maMethode() maMethode() // tout court
Sinon, MaClasse::maMethode() n'est pas spécialement propre, puisqu'on désactive explicitement le polymorphisme.
Jette aussi un coup d'œil à ça.
Non, mais il faut se souvenir que l'opérateur de portée est utile en cas d'ambigüité sur les noms de méthode.
Le même programme qu'au-dessus mais avec une classe « Abis » identique à la classe « A » et de même génération :
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68 #include <iostream> class A { public: virtual void MaMethode() { std::cout<<"A::MaMethode"<<std::endl; } virtual void MaSecondeMethode() { std::cout<<"A::MaSecondeMethode"<<std::endl; } void MaisPourquoi() { std::cout<<"A this->MaMethode()"<<std::endl; this->MaMethode(); std::cout<<"A::MaSecondeMethode()"<<std::endl; A::MaSecondeMethode(); } }; class Abis { public: virtual void MaMethode() { std::cout<<"Abis::MaMethode"<<std::endl; } virtual void MaSecondeMethode() { std::cout<<"Abis::MaSecondeMethode"<<std::endl; } void MaisPourquoi() { std::cout<<"Abis this->MaMethode()"<<std::endl; this->MaMethode(); std::cout<<"Abis::MaSecondeMethode()"<<std::endl; Abis::MaSecondeMethode(); } }; class B :public A , public Abis { public: virtual void MaMethode() { std::cout<<"B::MaMethode"<<std::endl; } virtual void MaSecondeMethode() { std::cout<<"B::MaSecondeMethode"<<std::endl; } }; int main(int argc, char* argv[]) { B b; // b.MaisPourquoi(); // Ambigü b.A::MaisPourquoi(); b.Abis::MaisPourquoi(); return 0; }
Obsidian : dans le cas où il y a ambiguité, en effet !
Mais je suis plutôt d'accord avec Ulmo : quand il n'y a pas d'ambiguité on perd en visibilité...
J'utilisais automatiquement "this" auparavant, puis on m'a parlé de trucs qui rendent méfiant : pointage foireux, ou autre. C'est assez flou ce qu'on m'a dit.
Euh, ça c'est tout sauf une bonne excuse. Un pointage foireux fera un appel foireux quelque soit la façon de faire.
Le plus important est de bien comprendre les différents mécanismes pour utiliser le plus adéquat. Je ne suis pas sûr qu'il y ait vraiment de réponse toute faite à ta question.
Je reformule ma question avec plus de restriction:
employer "this" dans une classe (ni abstraite, ni héritée, ni héritante, uniquement instanciée) est il mauvais ?
Dans quels cas ?
Est ce indispensable ?
Faut il préférer :plutôt queCode:MaClasse::
?Code:this->
Non.Citation:
employer "this" dans une classe (ni abstraite, ni héritée, ni héritante, uniquement instanciée) est il mauvais ?
Non.Code:Est ce indispensable ?
Non car "MaClasse::" ne s'utilise que dans des cas particulier(cité au dessus), donc ce n'est pas une habitude a prendre.Citation:
Faut il préférer :
plutôt queCode:MaClasse::
?Code:this->
Mais dans la majorité des cas l'appel d'une fonction membre sans "this->" ni "MaClasse::" suffit, et clarifie la syntaxe. (enfin ça c'est mon opinion personnelle)
Merci
: pléonasme...:mrgreen:Citation:
enfin ça c'est mon opinion personnelle
+1 et j'ajoute que ce n'est absolument pas une habitude à prendre, non seulement parce que ça ne sert à rien, mais parce que this désigne en soi un objet particulier. C'est idiot de le déréférencer si c'est pour se retrouver dans la situation initiale, et en plus, ça empêche l'utilisation de méthodes statiques !
Le seul cas similaire où j'utilise le this (et, là encore, ça risque de faire pousser les hauts-cris chez certains) est quand je veux donner les mêmes noms aux membres de ma classe et aux arguments de ma méthode :
Évidemment, ça dépend beaucoup du contexte, et dans la plupart des autres, cela peut s'avérer plus chiant qu'autre chose mais, en l'occurence, on comprend très bien qu'il s'agit des mêmes données, et on voit très bien les membres à gauche du signe égal et les arguments à sa droite.Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 class Objet { public: void Reinitialise (int,int); int x; int y; }; void Objet::Reinitialise (int x,int y) { this->x=x; this->y=y; }
A mes debuts sur C++, c'est exactement pour ca que j'utilisais "this", puis par extension je m'en servirai si il y a lieu dans les héritages.Citation:
Le seul cas similaire où j'utilise le this (et, là encore, ça risque de faire pousser les hauts-cris chez certains) est quand je veux donner les mêmes noms aux membres de ma classe et aux arguments de ma méthode
Je m'en suis en tous cas beaucoup servi pour tout emploi de méthode de MaClass dans les autres méthodes de MaClass, pour distinguer ainsi les méthodes statiques (qui ne possèdent alors aucun "préfixe"... et sont immédiatement repérable). Je perdrai cette (mauvaise) habitude (donc).
Mais je ne comprends pas ce que tu désignes par "déréférencer" à propos de this...
je pense que la question de base etait plutot entre appeler this->methode() et A::methodeStatique() donc sans instance.
en gros, faut il preferer les methodes statiques ?
Quand on n'a pas besoin d'utiliser des variables/méthodes de la classe dans la fonction, autant le faire, ça permet de faire des pointeurs de fonctions simples, éviter d'appeler une fonction membre avec la classe en paramètre (même si le compilateur optimise ça normalement), ...
Les deux fonctions compilent, et je préfère déclarer comme foo2 et pas comme foo1, du fait des avantages que j'ai énuméré ci-dessus:Code:
1
2
3
4
5
6
7 class A { public: //Ces fonctions n'accèdent pas aux membres void foo1() { cout << "hello world" << endl; } static void foo2() { cout << "hello world" << end; } };
- possibilité de faire un pointeur de fonction simple sur la fonction
- pas besoin de déréférencer l'objet à chaque appel de la fonction, bien que le compilateur optimise sûrement ça
Après c'est vrai que ce sont de maigres avantages qui ne servent pas souvent, et que c'est peut-être plus lisible d'omettre le mot-clé static.
Bref, chacun fait comme il veut en connaissance de cause.
Ce topic part un peu en sucette dû à la somme d'imprécisions qu'il recèle.
Pour répondre à la question initiale: MaClasse::f() et this->f() ne sont pas équivalents.
Lorsque l'on se situe dans le corps d'une fonction membre, chaque fonction appelée est implicitement appliquée à l'objet courant (si possible).
Donc this->f() et f() sont équivalents.
Notez que this->MaClasse::f() est une notation parfaitement correcte, c'est la forme explicite de l'appel ci-dessus.
@coyotte: Je ne suis pas d'accord avec toi. Une fonction membre qui ne dépend pas des données d'un objet courant est par essence "statique" et dois être déclarée comme telle. Ce n'est pas un problème d'implémentation, mais de conception. Pour ne pas polluer le topic, on peut en parler ailleurs.
Merci de recadrer (et même de répondre finalement) ce topic.
J'attire l'attention sur un terme ambiguë : "équivalent" ?
Equivelent comment : en terme de conception ? en terme de résultat à la compilation ? en terme de stabilité ?... bref, à préciser si possible.
J'aurais du écrire 'identique' à la place d'équivalent.
Lorsque tu écris le code suivant:
Le compilateur le transcrit le code de la fonction g() comme:Code:
1
2
3
4
5
6
7
8 class A { void f(); void g() { f(); } };
Pourquoi ? Simplement parce que les fonctions membres non statiques s'appliquent sur des instances de la classe en train de définir. Donc à chaque fois qu'un appel de fonction est présent dans le corps d'une fonction membre ( f() dans l'exemple ), le compilateur regardera s'il n'existe pas une fonction de membre de même nom qui puisse s'appliquer à l'objet courant ('this').Code:
1
2
3
4 void g() { this->f() }
Il y a de rares cas où il est impératif de préciser le 'this'. Mais dans chacun de ces cas, le compilateur protestera s'il est omit. Cela arrive principalement en cas d'héritage multiple (comme cité précédemment) ou lors de la définition de classes template. Cependant ce sont des problèmes un peu plus avancés et, une fois encore, rares.
Citation:
Pourquoi ? Simplement parce que les fonctions membres non statiques s'appliquent sur des instances de la classe en train de définir
+1 d'après moi.Citation:
Il y a de rares cas où il est impératif de préciser le 'this'. Mais dans chacun de ces cas, le compilateur protestera s'il est omit
Une question : si notre projet contient 5 classes, toute instanciée 1 seule fois, est il pertinent de faire des appels statiques ?
Que risque t on en faisant cela ?
La question ne se pose pas en ces termes. Si une fonction membre dépend de l'instance sur laquelle elle s'applique, elle ne doit pas être déclarée statique.
A contratio, si elle ne dépend d'aucune des variables d'instances de la classe, elle doit l'être.
Prends par exemple une classe qui représente un point 2D. Elle contient 2 variables d'instance x_ et y_ (l'abcisse et l'ordonnée du point).
Les fonctions membre x() et y() dépendent complètement de l'instance de la classe. Si je crée 2 instances de Point:Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 class Point { private: int x_; int y_; public: Point (int x, int y) : x_(x), y_(y) { /* EMPTY */ } int x() {return x_;} int y() {return y_;} static void message() { cout << "Je suis un point" << endl; } }
J'aurais bien deux résultats différents à origine.x() et p1.x().Code:
1
2 Point origine(0,0); Point p1(10, 26)
En revanche, la fonction message ne dépend aucunement des variables d'instance de Point. Quelque soit l'instance sur laquelle je l'applique, le résultat sera toujours l'affichage du même message.
C'est pourquoi au lieu de l'appeler sur une instance ( genre origine.message() ), je la déclare 'static' et je peux l'appeler via Point::message().
Ces 2 modes d'appels n'ont pas été crées pour faciliter l'écriture, mais bien pour exprimer 2 concepts de fonctions membres différents.
merci, tu m'as rassuré sur ma compréhension de l'Obj.Dev C++ et j'ai même appris des trucs...
:yaisse2::king:
this->isAGoodTopic :aie:
Salut, et désolé de faire remonter le topic, mais...
Le seul cas - en dehors des fonctions statiques où il est utile - et nécessaire - d'utiliser la syntaxe Type::fonction, c'est lorsque tu veux appeler explicitement le comportement propre à la classe mère, qu'il s'agisse d'une classe mère directe ou d'une classe "ancêtre"...
Un petit exemple pour bien me faire comprendre:
Soit la classe Mere, qui propose la fonction foo avec un comportement bien à elle comme:
et une classe Fille qui "enrobe" le comportement de la classe mère de son propre comportement comme:Code:
1
2
3
4
5
6
7
8
9
10 class Mere { public: /* on se fout pas mal des big fours pour l'exemple :D */ virtual void foo() { /* le comportement spécifique au type Mere */ std::cout<<"comportement spécifique a mere"<<std::endl; } };
Lorsque tu va jouer avec ces deux classes, sous une forme proche deCode:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 class Fille { public: virtual void foo() { /* ca, c'est propre à la classe Fille */ std::cout<<"début du comportement de la classe fille"<<std::endl; /* ca, c'est le comportement de la classe Mere que l'on veut * AUSSI avoir */ Mere::foo(); /* et enfin, le reste du comportement de la classe Fille */ std::cout<<"Et la fin du comportement de la classe Fille"<<std::endl; } };
tu obtiendra l'affichage suivant:Code:
1
2
3
4
5
6
7
8
9
10
11
12 int main() { /* une instance de la classe mere */ Mere m; /* une instance de la classe fille */ Fille f; std::cout<<"Jouons avec m"<<std::endl; m.foo(); std::cout<<"et maintenant avec f"<<std::endl; f.foo(); return 0; }
Bien sur, il t'appartient de décider si un tel genre de comportement est ce qui t'intéresse ;)Citation:
Jouons avec m
comportement spécifique a mere
et maintenant avec f
début du comportement de la classe fille
comportement spécifique a mere
Et la fin du comportement de la classe Fille
+1Citation:
Le seul cas - en dehors des fonctions statiques où il est utile - et nécessaire - d'utiliser la syntaxe Type::fonction, c'est lorsque tu veux appeler explicitement le comportement propre à la classe mère
exemple typique : A hérite de X :
Je code actuellement la GUI d'une appli, pour les méthodes paint() ou les callbacks resized(), c'est un cas récurrentCode:
1
2
3
4
5
6 X::methode(){ //autre code A::methode; //autre code };
Et plus généralement pour tous les overlloads (= override + rappel)