Il faut effectivement enlever le wdiget:: :)
Version imprimable
N'est-ce pas déjà trop tard de toute façon, pour appeler une fonction virtuelle?
À moins que cette restriction ne s'applique qu'aux constructeurs et pas au destructeur? Je ne sais jamais...
Non cela ne s'applique qu'aux constructeurs
Penser en C++
Citation:
Envoyé par Bruce Eckel
Oui desole pour l'erreur.
Je voulais bien sur dire que la restriction pour l'appel des fonctions virtuelles ne s'applique qu'aux destructeurs.
Je modifierai le code pour résoudre ce soucis, bien que ça ne soit pas le centre de l'article ;)
Oui perdu !!!!
Mais j'aurais appris qqchose !!!
Désolé de faire un petit remontage de post, mais j'aimerais répondre à l'un des interrogations de escafr, vu qu'il n'y a pas eu de réponse à priori...
Comme ça: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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84 #include <cstdlib> #include <iostream> struct NonClickable; struct Clickable { Clickable() { } Clickable( const NonClickable& ) { } void tell() { std::cout << "Clickable policy" << std::endl; } void click() { std::cout << "Clickable::clicked()" << std::endl; } }; struct NonClickable { NonClickable() { } NonClickable( const Clickable& ) { } void tell() { std::cout << "Nonclickable policy" << std::endl; } // pas de click(); }; template < class ClickPolicy > class Widget : public ClickPolicy { public: Widget(int id) : id_(id) { ClickPolicy::tell(); } template < class OtherClickPolicy > Widget( const Widget< OtherClickPolicy >& o ) : ClickPolicy( o ), id_( o.id_ ) { ClickPolicy::tell(); } int id_; }; int main( int argc, char* argv[] ) { Widget<Clickable> clickableWidget(1); clickableWidget.click(); Widget<NonClickable> nonClickableWidget( clickableWidget ); // nonClickableWidget.click(); compilera pas. // et l'inverse Widget< Clickable > clickableWidgetAgain( nonClickableWidget ); clickableWidgetAgain.click(); return 0; }
En réalité, tu ne dois modifier QUE ta classe Widget, ce qui est en fait quelque chose de naturel, vu que c'est l'hôte de la politique que tu veux ajouter.Citation:
Envoyé par escafr
Rien ne t'empêche de définir des politiques par défaut pour ta classe hôte, en mettant en premier ceux qui sont le plus modifiés. Les nouvelles politiques arrivant naturellement sur les dernières places, et possèdant leur comportement par défaut (quitte à ce que ça soit: "ne rien faire"), ça n'interfèrera pas avec le code existant.
Le nouveau code viendra s'installer naturellement, sans intérférer ou modifier le code existant, ce qui est ce qu'on recherche.
Exemple:
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89 struct RectangleDraw { void tell() { std::cout << "RectangleDraw policy" << std::endl; } void draw() { std::cout << " _________ " << std::endl; std::cout << "| |" << std::endl; std::cout << "|_________|" << std::endl; } }; struct SquareDraw { void tell() { std::cout << "SquareDraw policy" << std::endl; } void draw() { std::cout << " _____ " << std::endl; std::cout << "| |" << std::endl; std::cout << "| |" << std::endl; std::cout << "|_____|" << std::endl; } }; struct NoDraw { void tell() { std::cout << "NoDraw policy" << std::endl; } }; template < class ClickPolicy = NonClickable, class DrawingPolicy = NoDraw > class Widget : public ClickPolicy, public DrawingPolicy { public: Widget(int id) : id_(id) { policies_tell(); } template < class OtherClickPolicy, class OtherDrawPolicy > Widget( const Widget< OtherClickPolicy, OtherDrawPolicy >& o ) : ClickPolicy( o ), id_( o.id_ ) { policies_tell(); } void policies_tell() { DrawingPolicy::tell(); ClickPolicy::tell(); } int id_; }; int main( int argc, char* argv[] ) { Widget<> wdg(1); // wdg.click() ne compilera pas // wdg.draw() compilera pas // nouveau widget Widget< Clickable, RectangleDraw > crWdg(2); crWdg.click(); crWdg.draw(); return 0; }
En réalité, c'est justement l'inverse de l'effet recherché. Préfères-tu écrire une hiérarchie de N^2 classes à multiples héritages ou N classes sans redondance ?Citation:
Envoyé par escafr
L'héritage pure/multiple, dans ce cas-ci, va nous emmener dans pire que ce qu'on a: au lieu d'avoir un typage fort, on va quasiment pas en avoir, ou du moins pas de controle sur ce dernier.
Je préfère avoir des widgets de type différents dont les types souches sont génériques, c'est à dire Q< T, U, V, W > plutôt que QTUVW, QTUWV, QUTVW, QTUVW, pour ensuite m'amuser à upcaster tout le bordel et essayer de s'y retrouver...
Quant à l'héritage par interface unique, oui, mais tu dois alors souvent trop charger tes interfaces, ce qui est un autre problème en soi.
Ah oui j'avais zappé les questions désolé :oops:
Sinon, pour appuyer ce que dis Julien, je rajouterais que si tu as une classe Widget paramétrée par 4 politiques. Si la première a 3 implémentations, la deuxième 4, la troisième 2, et la quatrième 5, ça te fait 3*4*2*5 classes à implémenter si tu veux faire l'équivalent qu'avec l'héritage. C'est à dire 120 classes. Plutôt que justement 3+4+2+5 = 14 classes, qui ne seront que des petits bouts de classe en fait !
Entre 120 et 14, le choix pour moi est vite fait, et le sera probablement également pour un chef de projet correct :aie: