nraynaud lol | wpk a écrit :
pour le delete this,
faut faire gaffe à pas allouer ce genre d'objet sur la pile ou ne pas faire des compositions. C'est donc pas denué de risque qu'il vaudrait mieux eviter.
|
C'est pour ça que les constructeurs de Conteneur devraient être accessibles uniquement depuis wrappe_et_pointe().
Ceci dit, je n'ai découvert le problème des variables automatique que quelques posts plus haut, je n'y avais pas pensé, je n'ai pas l'habitude des langages qui créent des objets sur la pile !
Coup de bol, mon code ne pouvait pas en faire, mais il n'est malheureusement pas verrouillé.
Dernière minute : je viens de découvrir qu'on peut faire des classes internes en C++.
Refactoring du code suite à cette découverte :
Il faut mettre Conteneur dans Pointeur et RendezVous dans Agenda. Ca va déjà résoudre ce qui me chagrinait le plus.
Je viens de découvrir qu'il m'arrivait ce que je dénonçait plus haut, dans le destructeur de Conteneur, l'appel o << real suppose qu'il existe une surcharge de << pour le type de real, ce qui n'est bien entendu pas documenté par la déclaration de Conteneur. D'autre part, je vois pourquoi ils ont botté en touche sur la spécification de contrainte : la surcharge rend cette spécification extrèmement compliquée. Je sens que Meyer avait raison, la surcharge c'est casse-couilles (cf. le message d'erreur de << quand il trouve pas celui adapté à votre objet).
Comme RendezVous est devenu inaccessible à l'extérieur de Pointeur, j'ai dégagé l'interface commune Printable et ajouté un << en conséquence, ainsi, le conteneur peut prendre n'importe quoi qui soit membre de droite de << (pour les types primitifs par ex.) ou (en fait "y compris" ) n'importe quoi qui implante Printable. Printable elle-même est friend de << car elle ne possède pas d'état interne, c'est une simple interface. J'espère que ça n'ouvre pas l'état interne de ses implémenteurs à << mais je suppose que Stroustrup est pas con à ce point.
Cette fois-ci, le "delete this" devrait ne plus faire peur à personne, il est (si je me plante pas) impossible de créer un Conteneur hors des conditions strictes de wrappe_et_pointe().
Bien entendu, le main() ne bouge pas, de même que la modification de Pointeur ne modifie pas Agenda. Principe ouvert-fermé oblige.
Code :
- /*agenda.C un petit exemple d'agenda avec des rendez-vous gérés par smart-pointeurs*/
- using namespace std;
- #include <string.h>
- #include <stdlib.h>
- #include <list>
- #include <string>
- #include <iostream>
- class Carnet; // le carnet de rendez-vous
- template<typename T> class Pointeur; // le pointeur "intelligent" (si tant est que cette technique est intelligente)
- class Printable;
- //on wrappe un peu les notations lourdes
- typedef Pointeur<const string> ConstStrPtr;
- ostream & operator << (ostream &o, const Printable &p);
- class Printable {
- friend ostream & operator << (ostream &o, const Printable &p);
- public :
- virtual ostream & printOn(ostream & o) const = 0;
- virtual ~Printable(){};
- };
- template<typename T> class Pointeur {
- // T doit être un membre de droite possible de <<
- // T de type Printable est suffisant.
- private:
- class Conteneur {
- private :
- T real; //l'instance, sans indirection
- unsigned int refcount; //le compteur de références
- public :
- Conteneur():refcount(0) {}
- Conteneur(const T &r):real(r), refcount(0) {}
- Conteneur(Conteneur &c):real(c.real),refcount(0){}
- ~Conteneur(){
- cout << "DESTRUCTION DE :" << endl;
- cout << real << endl;
- cout << "destruction du conteneur" << endl;
- cout << "**********" << endl << endl;
- }
- void unbind() { // un pointeur s'en va
- refcount--;
- if (refcount == 0)
- delete this;
- }
- void bind() { // un nouveau pointeur dessus
- refcount++;
- }
- T* operator & () { // on planque le conteneur
- return ℜ
- }
- };
- Conteneur *real;
- Pointeur(Conteneur *p) :real(p) {p->bind();}
- public :
- Pointeur():real(0) {}
- Pointeur(T p) :real(new Conteneur(p)) {real->bind();}
- Pointeur(const Pointeur &r) :real(r.real) {if (r.real != 0) r.real->bind();}
- ~Pointeur() { // quand on meurt, on décrémente
- if (real != 0)
- real->unbind();
- }
- Pointeur operator = (Pointeur r) { // ici est toute la feinte
- if (r.real != real) {
- if (real)
- real->unbind();
- if (r.real)
- r.real->bind();
- real = r.real;
- }
- return r;
- }
- T * operator -> (void) const { // on cache le système smart-pointeur/conteneur
- return &(*real);
- }
- T & operator * (void) const { // pareil, sauf qu'on renvoie l'instance
- return *(&(*real)); //oui, c'est comique
- }
- bool operator == (const Pointeur &p) const { // égalité de pointeur
- return real == p.real;
- }
- bool operator != (const Pointeur &p) const { // différence de pointeurs
- return real != p.real;
- }
- static Pointeur<T> wrappe_et_pointe(T &c) {
- return Pointeur(new Conteneur(T(c)));
- }
- };
- class Carnet : public Printable {
- private:
- class RendezVous : public Printable {
- private:
- ConstStrPtr nom;
- ConstStrPtr num_tel;
- ConstStrPtr lieu;
- ConstStrPtr date;
-
- public:
- RendezVous(const RendezVous &r):Printable(), nom(r.nom), num_tel(r.num_tel), lieu(r.lieu), date(r.date){};
- RendezVous(ConstStrPtr _nom, ConstStrPtr _num_tel, ConstStrPtr _lieu, ConstStrPtr _date)
- :nom(_nom), num_tel(_num_tel), lieu(_lieu), date(_date){};
- ostream & printOn(ostream & o) const {
- o << "nom : " << *nom << endl;
- o << "tel : " << *num_tel << endl;
- o << "lieu : " << *lieu << endl;
- o << "date : " << *date;
- return o;
- }
- ConstStrPtr get_date(void) const {
- return date;
- }
- };
- //on wrappe un peu les notations lourdes
- typedef Pointeur<const RendezVous> ConstRdvPtr;
- typedef list<ConstRdvPtr > ListRdv;
- typedef ListRdv::const_iterator ConstIterList;
- typedef ListRdv::iterator IterList;
- ListRdv rdvs; // la collection de rendez-vous
-
- void insert(ConstRdvPtr r) {
- rdvs.push_back(r);
- }
- public :
- Carnet(void) {};
- void add(ConstStrPtr nom, ConstStrPtr num_tel, ConstStrPtr lieu, ConstStrPtr date) {
- ConstRdvPtr p = ConstRdvPtr::wrappe_et_pointe(RendezVous(nom, num_tel, lieu,date));
- insert(p);
- }
- //recherche une date précise et insère tous ceux qui correspondent dans c
- void cherche_date(Carnet &c, ConstStrPtr date) const {
- for (ConstIterList i = rdvs.begin(); i != rdvs.end(); ++i)
- if (!(*i)->get_date()->compare(*date))
- c.insert(*i);
- }
- // supprime de l'instance courante tous les rendez-vous contenus dans c
- //passe silencieusement s'il ne trouve pas
- void supprimer(const Carnet &c) {
- for(ConstIterList j = c.rdvs.begin(); j != c.rdvs.end(); ++j) {
- ConstRdvPtr toDie = *j;
- for(IterList i = rdvs.begin(); i != rdvs.end(); ++i)
- if (*i == toDie) {
- rdvs.erase(i);
- break;
- }
- }
- }
- //ceux qui font du smalltalk savent :-)
- ostream &printOn(ostream & o) const {
- o << "--- carnet ---" << endl;
- for (ConstIterList i = rdvs.begin(); i != rdvs.end(); ++i)
- o<< (**i) << endl << endl;
- o << "--------------" << endl;
- return o;
- }
- };
- ostream & operator << (ostream & o, const Printable &p) {
- return p.printOn(o);
- }
- //un peu d'utilitaire
- ConstStrPtr ps_from_c(char *s) {
- return ConstStrPtr::wrappe_et_pointe(string(s));
- }
- int main() {
- Carnet carnet;
- //quelques pointeurs partagés pour tromper l'ennemi
- ConstStrPtr dsc = ps_from_c("DSC" );
- ConstStrPtr onze_dec = ps_from_c("11/12/2002" );
- ConstStrPtr tel = ps_from_c("02 97 15 40 56" );
- carnet.add(ps_from_c("Roger" ) , tel, ps_from_c("DTC" ), ps_from_c("11/12/2012" ));
- carnet.add(ps_from_c("Robert1" ), tel, dsc, onze_dec);
- carnet.add(ps_from_c("Robert2" ), tel, dsc, onze_dec);
- carnet.add(ps_from_c("Robert3" ), tel, dsc, ps_from_c("11/12/2052" ));
- carnet.add(ps_from_c("Robert4" ), tel, dsc, onze_dec);
- cout << "contenu du carnet : " << endl;
- cout << carnet;
- { // un petit bloc pour montrer les variables automatiques
- Carnet c2;
- cout << "recherche par fonction custom de la date 11/12/2002" << endl;
- carnet.cherche_date(c2, *onze_dec);
- cout << c2 << endl;
- cout << "tentative de suppression des rendez-vous du 11/12/2002" << endl;
- carnet.supprimer(c2);
- cout << "sortie du bloc" << endl;
- } // à la sortie, c2 est détruit, les rendez-vous de c2 ne sont plus dans carnet, ils sont détruits automatiquement
- cout << "on est hors du bloc" << endl;
- cout << "restent :" << endl;
- cout << carnet;
- cout << "sortie du programme" << endl;
- return 0;
- }
|
merci pour ces commentaires, j'espère que vous en avez d'autres ... y'a sûrement encore moyen de faire mieux. |