IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

Smart Pointer


Sujet :

C++

  1. #1
    Fry
    Fry est déconnecté
    Membre confirmé Avatar de Fry
    Profil pro
    Inscrit en
    Juin 2003
    Messages
    150
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2003
    Messages : 150
    Par défaut Smart Pointer
    Bonjour

    J'essaie de developper un FrameWork Oriente Object en C++ et pour ce faire j essaie d'utiliser les smart pointer avec un object de base gerant un compteur de reference et un type de pointer specifique permettant de liberer automatiquement l'object.

    Comme je debute sur l'implementation de Smart Pointer et aussi sur les template j aimerai bien savoir si ce type d'implementation est valide et ne risque pas de me poser des probleme a terme.

    J'ai regarde le code de Loki et il me semble y avoir le meme type de smart pointer cependant j ai du mal a suivre le code
    et je n ai pas vu dans Boost une implementation avec une classe Object de base

    Fichier Pointer.h
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
     
    #ifndef FRAMEWORK_POINTER_____
    #define FRAMEWORK_POINTER_____
     
    #include "Object.h"
     
    namespace foundation
    {
    	template <class T>
    	class MyPointer
    	{
    	private:
    		MyObject* ObjectPointe;
     
    	public:
    		//Constructeur
    		inline MyPointer() : ObjectPointe(NULL)
    		{ }
    		inline MyPointer(const MyObject* inPtr) : ObjectPointe( (inPtr != NULL) ? (const_cast<MyObject*>(inPtr)->refer()) : NULL )
    		{ }
    		inline MyPointer( const MyPointer& inPtr ) : ObjectPointe( (inPtr.getPointer() != NULL) ? (const_cast<foundation::MyObject*>(inPtr.getPointer())->refer()) : NULL )
    		{ }
    		inline ~MyPointer()
    		{
    			if ( this->ObjectPointe != NULL ) this->ObjectPointe->unrefer();
    			this->ObjectPointe = NULL;
    		}
     
    		inline T* getPointer()
    		{
    			return static_cast<T*>( this->ObjectPointe);
    		}
    		inline const T* getPointer() const
    		{
    			return static_cast<T*> (this->ObjectPointe);
    		}
     
    		inline MyPointer& operator=(const MyObject* inPtr)
    		{
    			if (this->ObjectPointe == inPtr) return *this; // si le pointeur pointe deja sur l'object
    			if ( this->ObjectPointe != NULL ) this->ObjectPointe->unrefer(); // liberation de l'object deja pointe
    			if ( inPtr )
    				this->ObjectPointe = const_cast<T*>(inPtr)->refer();
    			else this->ObjectPointe = NULL;
    			return *this;
    		}
    		inline MyPointer& operator=(const MyPointer& inPtr)
    		{
    			if ( this==&inPtr ) return *this;
    			if ( this->ObjectPointe == inPtr.getPointer() ) return *this;
    			if ( this->ObjectPointe )
    				this->ObjectPointe->unrefer();
    			if ( inPtr.getPointer() != NULL )
    				this->ObjectPointe = const_cast<T*>(inPtr.getPointer())->refer();
    			else this->ObjectPointe = NULL;
    			return *this;
    		}
     
    		inline T& operator*()
    		{
    			return *(static_cast<T*> (this->ObjectPointe));
    		}
    		inline const T& operator*() const
    		{
    			return *(static_cast<T*> (this->ObjectPointe));
    		}
    		inline T* operator->()
    		{
    			return static_cast<T*> (this->ObjectPointe);
    		}
    		inline const T* operator->() const
    		{
    			return static_cast<T*> (this->ObjectPointe);
    		}
     
    		inline bool operator!() const
    		{
    			return (!this->ObjectPointe);
    		}
    		inline bool operator==(const MyObject* inPtr) const
    		{
    			return ( this->ObjectPointe == inPtr);
    		}
    		inline bool operator==(const MyPointer& inPtr) const
    		{
    			return ( this->ObjectPointe == inPtr.getPointer());
    		}
    		inline bool operator!=(const MyObject* inPtr) const
    		{
    			return (this->ObjectPointe != inPtr);
    		}
    		inline bool operator!=(const MyPointer& inPtr) const
    		{
    			return ( this->ObjectPointe != inPtr.getPointer());
    		}
    	};
    }
     
    #endif
    fichier Object.h
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
     
    #ifndef FRAMEWORK__BASEOBJECT__
    #define FRAMEWORK__BASEOBJECT__
     
    #include <iostream>
     
    namespace foundation  
    {
    	// forward declaration
    	template <class T>class MyPointer;
     
    	class MyObject
    	{
    	private:
    		mutable unsigned int compteur; 
     
    	public:
    		// Constructeur
    		MyObject() : compteur(0)
    		{ 
    			std::cout << "Construction Base" << std::endl;
    		}
     
    		MyObject( const MyObject& inCopy ) : compteur(0)
    		{ 
    			std::cout << "Construction Base" << std::endl;
    		}
     
    		virtual ~MyObject()
    		{
    			std::cout << "Destruction base" << std::endl;
    		}
     
    		inline unsigned int getRefCounter() const
    		{
    			return this->compteur;
    		}
    		inline MyObject* refer()
    		{
    			++this->compteur;
    			return this;
    		}
    		inline void unrefer()
    		{
    			if ((--this->compteur) == 0) delete this;
    		}
    	};
    }
    #endif
    De plus pour que le FrameWork soit complet est il indispensable de definir une classe Allocator avec une interface comme ca:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    class Allocator : public Object {
    public:
    virtual Object* allocate() const =0;
    virtual Object* clone(const Object&) const =0;
    virtual void copy(Object&, const Object&) const =0;
    };
    Quel en est l'utilite est ce uniquement pour permettre la gestion de conteneur?

    et sinon ou est ce que je peu trouver des exemple de l'utilisation des smart pointer de Loki?

  2. #2
    Expert confirmé
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 296
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 296
    Par défaut
    Notes en vrac:
    - Si tu veux tout savoir sur Loki, Modern C++ Design d'Andrei Alexandrescu est le bouquin qu'il te faut -- publié chez Addisson Wesley. Les polices sont la clé. Un Loki::SmartPtr<> est une classe qui est réglée avec des polices qui vont permettre de choisir parmi diverses politiques orthogonales : comment est stocké l'objet, comment sont comptées (si elles le sont) les références, type de copie, ...
    Je ne sais plus si ce chapitre n'était pas dispo quelques part (genre informit) en tant que chapitre exemple du bouquin.

    - Avoir un classe racine mère de toutes choses est une hérésie. Sérieux! C'est le void* du monde objet. Avec un peu de chance, je n'ai pas compris ce que tu voulais dire avec ton Object
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  3. #3
    Fry
    Fry est déconnecté
    Membre confirmé Avatar de Fry
    Profil pro
    Inscrit en
    Juin 2003
    Messages
    150
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2003
    Messages : 150
    Par défaut
    Je ne comprend pas vraiment en quoi c'est une hérésie?
    Faire deriver toutes mes classes d'une classe mere gerant les smart pointer me semblait une bonne idée...
    D'ailleur je croi qu il existe deja des lib utilisant cette technique. Sinon des que je peu j croi qu il va falloir que j'achete ce bouquin mais il existe pas en francais?

  4. #4
    Expert confirmé
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 296
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 296
    Par défaut
    C'est une hérésie dans les sens où :
    - Tu perds ton typage fort et te retrouve parfois à downcaster inutilement -- cf les collections en Java et avec la VCL
    - Tu forces un objet à avoir des rôles qui n'ont parfois aucun sens pour lui -- et si tu ne veux pas qu'il ait ce rôle, alors il ne faut surtout pas hériter
    - Tu mélanges des sémantiques : aucun sens pour une classe matrice de devoir supporter une dérivation publique (=> à tendance polymorphe) alors que l'on attend qu'une telle classe dispose d'une sémantique de valeur et non de référence.
    - limitation abusive du framework -- pourquoi ne pourrait-on pas avoir des smart-pointeurs sur autre chose que des objets, et même des objets bien précis ?
    - et certainement d'autres points que je n'ai pas en tête à cette heure ci.


    Quand aux libs qui utilisent ça, elles répondent à un ou plusieurs des points suivants :
    - langages ne supportant pas la généricité
    - langages ne supportant pas l'héitage public
    - compatibilité avec un vieux C++ d'avant les templates
    - mauvais design

    Je crois qu'une traduction de Modern C++ Design est en cours, mais je n'en sais pas plus. Voir sur le site de l'auteur.
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  5. #5
    Expert confirmé

    Homme Profil pro
    pdg
    Inscrit en
    Juin 2003
    Messages
    5 756
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : pdg

    Informations forums :
    Inscription : Juin 2003
    Messages : 5 756
    Billets dans le blog
    3
    Par défaut
    J'ai moi aussi exploré une classe smart ptr via héritage, voici ce que j'avais fait:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
     
    // déclaration de la classe smart pointeur
    template<typename T>
    class Ptr;
     
    // la classe dont on hérite et qui sert à stocker
    // le compteur
    template<typename T>
    class PtrBase
    {
    public:
        typedef Ptr<T> ptr;
     
    protected:
        PtrBase() : count( 0 )
        {
        }
     
        virtual ~PtrBase()
        {
            assert( this->count == 0 );
        }
     
    private:
        // donner accès à la classe smart ptr pour qu'elle
        // gère le compteur
        friend class Ptr<T>;
        int count;
    };
     
    // implémentation de la classe smart pointeur
    template<typename T>
    class Ptr
    {
    public:
        Ptr( T * t ) :
             ptr( t )
        {
            ++this->ptr->count;
        }  
     
        Ptr( const Ptr<T> & p ) :
             ptr( p.ptr )
        {
            ++this->ptr->count;
        }
     
        ~Ptr( )
        {
            if ( this->ptr &&
                 --this->ptr->count == 0 )
            {
                 delete this->ptr;     
            }
        }   
     
        T * operator ->()
        {
            assert( this->ptr );
            return this->ptr;
        }
     
        void reset()
        {
            if ( this->ptr )
            {
                --this->ptr->count;
                this->ptr = 0;
            }
        }
     
    private:
         T *ptr;  
    };
    ça s'utilise ainsi:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
     
    class A : public PtrBase<A>
    {
    public:
        A( int )
        {
            cout << "A::A()\n";
        } 
     
        ~A()
        {
            cout << "A::~A()\n";
        }  
     
        int value;
    };
     
     
    int main()
    {
        A::ptr a1 = new A( 10 );
        A::ptr a2 = a1;
        a1->value = 0;
        a1.reset();
        cout << a2->value << '\n';
    }
    tu as lu l'article dont parle Luc ?
    http://www.informit.com/articles/article.asp?p=31529&rl=1

  6. #6
    Membre habitué
    Inscrit en
    Mars 2005
    Messages
    16
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 16
    Par défaut
    Ici ma version (non testée)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    class reference_counted
    {
    public:
      reference_counted():
        count(0){}
      reference_counted(const reference_counted&):
        count(0){}
      reference_counted&operator=(const reference_counted&){
        return *this;
      }
      // destructeur n'a pas besoin d'être virtuel car
      // je ne convertis jamais T en reference_counted
      // donc le T est toujours détruit en tant que T et
      // non pas en tant que reference_counted.
      void alloc()const{
        ++count;
      }
      void free()const{
        if(!--count)
          delete this;
      }
      // Elimine les test si t est NULL
      static reference_counted*get_null(){
        // n'est jamais détruit pas delete car
        // count ne sera jamais 0.
        static reference_counted null(1);
        return &null;
      }
    private:
      reference_counted(unsigned count):
        count(count){}
      mutable unsigned count;
    };
     
    template<class T>
    class lock_ptr
    {
    public:
      // inline est implicit et ne réduirait que la
      // lisiblité.
      lock_ptr():
        t(reference_counted::get_null()){
        t->alloc();
      }
      // T2 n'as pas besoin d'être un T mais peut aussi
      // être un enfant de T.
      template<class T2> 
      explicit lock_ptr(T2*t):
        t(t ? t : reference_counted::get_null()){
        t->alloc();
      }
      template<class T2> 
      lock_ptr(const lock_ptr<T2>&other):
        t(other.t){
        t->alloc();
      }
      // Copy and swap idom (recherche internet pour plus
      // de details)
      void swap(lock_ptr&other)throw(){
        std::swap(t, other.t);
      }
      template<class T2>
      lock_ptr&operator=(const lock_ptr<T2>&other){
        lock_ptr temp(other);
        swap(temp);
        return *this;
      }
     
      // tous les opérateurs et blabla
     
      ~lock_ptr(){
        t->free(); 
      }
    private:
      // T et non pas reference_counted. Pour l'un
      // cela a pour effet que le destructeur de 
      // reference_counted n'a pas besoin d'être virtuel.
      // Pour l'autre reference_counted serait toujours soit
      // const soit non const. T cependant peut varier.
      // lock_ptr<int> est aussi valable que lock_ptr<const int>
      T*t;
    };
    Il exist une autre méthode pour implementer ce pointeur sans utiliser une classe de base. Tous les pointeurs forment une double liste chainée circulaire. Enlever un pointer (c'est à dire le détuire) est une opération constante et insérer un nouveau est aussi constant. Si le dernier élement de cette chaine est enlevé l'objet est détruit. boost::shared_ptr utilise par exemple cette stratégie.

    Même si la stratégie évitant la classe de base est à préférer en général celle présenté ici est d'un facteur constant d'au moins 2 plus rapide. Dans certaines situtions cela justifie l'utilisation d'une classe de base. Cependant on peut ce demander si on ne devrai pas préférer une toute autre stratégie de genstion de mémoire.

Discussions similaires

  1. Réponses: 2
    Dernier message: 19/03/2008, 11h25
  2. Boost & smart pointers
    Par Goduak dans le forum Boost
    Réponses: 5
    Dernier message: 28/02/2008, 10h41
  3. Smart Pointer et le Multithreading
    Par adel_dz dans le forum C++
    Réponses: 12
    Dernier message: 19/03/2007, 10h23
  4. Utilisation des smart pointer
    Par 0xYg3n3 dans le forum MFC
    Réponses: 11
    Dernier message: 22/04/2005, 18h37
  5. templates et smart pointers
    Par delire8 dans le forum C++
    Réponses: 9
    Dernier message: 10/07/2003, 16h26

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo