shared_ptr ou comment éviter les fuites mémoires en C++ (3/3)

Pour finaliser plus proprement mon exemple de conversion d’un objet managé en pointeur natif void*, sans avoir à se soucier de la gestion de la destruction, j’ai donc “encapsulé” cette logique dans une classe template native. Cette classe, que j’ai nommé cli_ptr, va donc me permettre de :
– récupérer simplement un void* à partir d’une instance d’un objet managé,
– récupérer simplement une instance de cette classe à partir d’un void* (qui doit tout de même être à la base obtenue par la même mécanique du GChandle)
– récupérer simplement une instance de l’objet managé directement typé (d’où la classe template qui me permettra de prendre en charge le cast)
– tout ça sans jamais à avoir me poser la question de la gestion de la mémoire !

Voilà pour un exemple plus parlant, un code qui utilise ma classe :

for(int i=0;i<99999999;i++)
        {

        //on cree un cli_ptr templatisé avec un objet managé. Le tout dans un shared_ptr
        shared_ptr<cli_ptr<String>> ptr = cli_ptr<String>::Create(gcnew String("hello leaks !"));

        for(int j=0;j<99;j++)
        {
        //on obtient l'objet managé transformé en void* par la mecanique du GCHandle interne
        void* natptr = ptr->GetNative();

        //à partir de ce "type" de void* on peut recree un cli_ptr "templatisé"
        shared_ptr<cli_ptr<String>> ptr2 =  cli_ptr<String>::Create(natptr);

        //on récupère notre objet managé typé sur le cli_ptr
        String^ mptr = ptr2->GetManaged<String>();

        Console::WriteLine(mptr);
        }
        }

Vous constatez donc que j’utilise le shared_ptr pour stocker une instance de ma classe cli_ptr, ça c’est pour prendre les bonnes habitudes de ne plus faire de new ou de delete ! En plus ma classe cli_ptr propose 2 méthode static “Create” qui prennent soit le pointeur managé soit le pointeur natif. Enfin cette classe propose 2 methodes d’instance qui permettent de récupérer le pointeur natif et le pointeur managé. En interne cette classe utilise un shared_ptr<void> pour stocker le pointeur natif (obtenu avec le GCHandle) et ce shared_ptr est crée en fournissant un deleter pour détruire correctement le GCHandle (voir part 2/3 de cette série de post c’est quasi le même code mais mieux “rangé”)

Bon trêve de blabla, voilà le code de la classe en question avec quelques commentaires, ça sera j’espère suffisamment parlant:

#include <memory>
        #include <vcclr.h>
        using namespace System;
        using namespace System::Runtime::InteropServices;
        using namespace std::tr1;

        #pragma once

        //methode de destruction d'un void* qui provient d'un GCHandle
        void Deleter(void* p)
        {
        IntPtr ptr = IntPtr(p);
        GCHandle gcPtr = GCHandle::FromIntPtr(ptr);
        if(gcPtr.IsAllocated)
        {
        gcPtr.Free();
        }
        }

        template <class N> class cli_ptr
        {
        private:
        //pointeur de void* stocké en tant que shared_ptr
        shared_ptr<void> m_natptr;

        public:
        //methode statique de creation d'un cli_ptr à partir d'un objet managé.
        static shared_ptr<cli_ptr> Create(Object^ mngdObj)
        {
        shared_ptr<cli_ptr> p(new cli_ptr(mngdObj));
        return p;
        }

        //methode statique de creation d'un cli_ptr à partir d'un void*
        // Attention ce void* doit provenir à la base d'un objet managé passé à un GCHandle
        static shared_ptr<cli_ptr> Create(void* natObj)
        {
        IntPtr ptr = IntPtr(natObj);
        GCHandle gcPtr = GCHandle::FromIntPtr(ptr);
        shared_ptr<cli_ptr> p;
        if(gcPtr.IsAllocated)
        {
        p = shared_ptr<cli_ptr>(new cli_ptr(gcPtr.Target));
        }
        return p;
        }

        //constructeur. à partir de l'instance d'objet managé, on obtient un void* qu'on garde dans notre shared_ptr membre privé
        cli_ptr(Object^ mngdObj)
        {
        GCHandle gcPtr = GCHandle::Alloc(mngdObj);
        IntPtr ptr = GCHandle::ToIntPtr(gcPtr);
        shared_ptr<void> sp(shared_ptr<void>(ptr.ToPointer(), Deleter));
        m_natptr = sp;
        }

        //methode qui recupère le void*
        void* GetNative()
        {
        if(m_natptr)
        return m_natptr.get();
        else
        return NULL;
        }

        //methode qui récupère le pointeur managé et fait le cast directement dans le bon type par le biais du template
        template<class N> N^ GetManaged()
        {
        if(m_natptr)
        {
        IntPtr ptr = IntPtr(m_natptr.get());
        GCHandle gcPtr = GCHandle::FromIntPtr(ptr);
        if(gcPtr.IsAllocated)
        {
        return dynamic_cast<N^>(gcPtr.Target);
        }
        else
        return nullptr;
        }
        else
        return nullptr;

        }

        };
        

Je ne remet pas ça avec le graph perfmon, car c’est toujours le calme plat question consommation mémoire ! vive le shared_ptr !!

voi aussi shared_ptr ou comment éviter les fuites mémoires en C++ 1/3

et shared_ptr ou comment éviter les fuites mémoires en C++ 2/3

Publié dans Développement divers Tagués avec : ,

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

*

Verifions que vous êtes un humain * Time limit is exhausted. Please reload CAPTCHA.

Archives

Social

  • Twitter
  • LinkedIn
  • Flux RSS
  • mvp
  • technet
  • Google+