Partager via


Pointeurs intelligents (C++ moderne)

Dans la programmation C++ moderne, la bibliothèque Standard inclut des pointeurs intelligents, qui sont utilisés pour vous assurer que les programmes sont libres de mémoire et de fuites de ressources et sont sans risque d’exception.

Utilisations pour les pointeurs intelligents

Les pointeurs intelligents sont définis dans l’espace std de noms dans le fichier d’en-tête <de mémoire> . Ils sont essentiels à l’idiome de programmation RAII ou Resource Acquisition Is Initialisation . L'objectif principal de cet idiome est de garantir que l'acquisition des ressources se produit en même temps que l'initialisation de l'objet, afin que toutes les ressources de l'objet soient créées et préparées en une seule ligne de code. En pratique, le grand principe de RAII est de donner la propriété de toute ressource allouée par tas, par exemple la mémoire allouée dynamiquement ou les handles d'objet système, à un objet alloué par la pile dont le destructeur contient le code de suppression ou de libération de la ressource ainsi que le code de nettoyage associé.

Dans la plupart des cas, lorsque vous initialisez un pointeur brut ou un handle de ressource pour pointer vers une ressource réelle, passez immédiatement le pointeur à un pointeur intelligent. En C++ moderne, les pointeurs bruts sont utilisés uniquement dans les petits blocs de code à portée limitée, les boucles ou les fonctions d'assistance lorsque les performances sont essentielles et qu'il n'existe aucune chance de confusion en matière de propriété.

L'exemple suivant compare une déclaration de pointeur brut à une déclaration de pointeur intelligent.

void UseRawPointer()
{
    // Using a raw pointer -- not recommended.
    Song* pSong = new Song(L"Nothing on You", L"Bruno Mars"); 

    // Use pSong...

    // Don't forget to delete!
    delete pSong;   
}

void UseSmartPointer()
{
    // Declare a smart pointer on stack and pass it the raw pointer.
    unique_ptr<Song> song2(new Song(L"Nothing on You", L"Bruno Mars"));

    // Use song2...
    wstring s = song2->duration_;
    //...

} // song2 is deleted automatically here.

Comme indiqué dans l'exemple, un pointeur intelligent est un modèle de classe que vous déclarez dans la pile et que vous initialisez en utilisant un pointeur brut qui pointe vers un objet alloué par tas. Une fois le pointeur intelligent initialisé, il possède le pointeur brut. Cela signifie que le pointeur intelligent est chargé de supprimer la mémoire que le pointeur brut spécifie. Le destructeur du pointeur intelligent contient l'appel à supprimer, et comme le pointeur intelligent est déclaré sur la pile, son destructeur est appelé lorsque le pointeur intelligent sort de son étendue, même si une exception est levée à un emplacement situé plus haut dans la pile.

Accédez au pointeur encapsulé en utilisant des opérateurs de pointeur familiers, -> et *, que la classe de pointeur intelligent surcharge pour retourner un pointeur brut encapsulé.

L'idiome de pointeur intelligent C++ ressemble à la création d'objets dans les langages tels que C# : vous créez l'objet, puis vous laissez au système le soin de le supprimer au moment approprié. La différence vient du fait qu'aucun récupérateur de mémoire distinct ne s'exécute en arrière-plan ; la mémoire est gérée via les règles de portée C++ standard afin que l'environnement d'exécution soit plus rapide et plus efficace.

Important

Veillez à toujours créer les pointeurs intelligents sur une ligne distincte de code et jamais dans une liste de paramètres, afin qu'une fuite de ressource subtile ne se produise pas suite à certaines règles d'allocation de liste de paramètres.

L’exemple suivant montre comment un unique_ptr type de pointeur intelligent de la bibliothèque standard C++ peut être utilisé pour encapsuler un pointeur vers un objet volumineux.


class LargeObject
{
public:
    void DoSomething(){}
};

void ProcessLargeObject(const LargeObject& lo){}
void SmartPointerDemo()
{    
    // Create the object and pass it to a smart pointer
    std::unique_ptr<LargeObject> pLarge(new LargeObject());

    //Call a method on the object
    pLarge->DoSomething();

    // Pass a reference to a method.
    ProcessLargeObject(*pLarge);

} //pLarge is deleted automatically when function block goes out of scope.

L'exemple illustre les étapes essentielles suivantes pour utiliser les pointeurs intelligents.

  1. Déclarez le pointeur intelligent comme variable automatique (locale). (N’utilisez pas l’expression ou malloc le new pointeur intelligent lui-même.)

  2. Dans le paramètre de type, spécifiez le type pointé du pointeur encapsulé.

  3. Passez un pointeur brut à un newobjet -ed dans le constructeur de pointeur intelligent. (Certaines fonctions utilitaires ou certains constructeurs de pointeur intelligent le font à votre place.)

  4. Utilisez les opérateurs -> et * surchargés pour accéder à l'objet.

  5. Laissez le pointeur intelligent supprimer l'objet.

Les pointeurs intelligents sont conçus pour être aussi efficaces que possible, aussi bien en termes de mémoire que de performances. Par exemple, la seule donnée membre dans unique_ptr est le pointeur encapsulé. Cela signifie que unique_ptr a exactement la même taille que ce pointeur, soit quatre octets ou huit octets. L’accès au pointeur encapsulé à l’aide du pointeur intelligent surchargé * et -> les opérateurs ne sont pas beaucoup plus lents que d’accéder directement aux pointeurs bruts.

Les pointeurs intelligents ont leurs propres fonctions membres, qui sont accessibles à l’aide de la notation « dot ». Par exemple, certains pointeurs intelligents de la bibliothèque C++ Standard ont une fonction membre de réinitialisation qui libère la propriété du pointeur. C'est utile lorsque vous souhaitez libérer la mémoire possédée par le pointeur intelligent avant que celui-ci ne passe hors de portée, comme illustré dans l'exemple suivant.

void SmartPointerDemo2()
{
    // Create the object and pass it to a smart pointer
    std::unique_ptr<LargeObject> pLarge(new LargeObject());

    //Call a method on the object
    pLarge->DoSomething();

    // Free the memory before we exit function block.
    pLarge.reset();

    // Do some other work...

}

Les pointeurs intelligents permettent généralement d'accéder directement à leur pointeur brut. Les pointeurs intelligents de la bibliothèque standard C++ ont une get fonction membre à cet effet et CComPtr ont un membre de classe public p . En assurant l'accès direct au pointeur sous-jacent, vous pouvez utiliser le pointeur intelligent pour gérer la mémoire dans votre propre code et continuer à passer le pointeur brut au code qui ne prend pas en charge les pointeurs intelligents.

void SmartPointerDemo4()
{
    // Create the object and pass it to a smart pointer
    std::unique_ptr<LargeObject> pLarge(new LargeObject());

    //Call a method on the object
    pLarge->DoSomething();

    // Pass raw pointer to a legacy API
    LegacyLargeObjectFunction(pLarge.get());    
}

Types de pointeurs intelligents

La section suivante résume les différents genres de pointeurs intelligents disponibles dans l'environnement de programmation Windows, et indique quand les utiliser.

Pointeurs intelligents de la bibliothèque standard C++

Utilisez ces pointeurs intelligents comme premier choix pour encapsuler les pointeurs vers les objets C++ anciens ordinaires (POCO).

  • unique_ptr
    Autorise exactement un propriétaire du pointeur sous-jacent. À utiliser comme option par défaut pour POCO à moins que vous ne soyez certain d'avoir besoin de shared_ptr. Peut être déplacé vers un nouveau propriétaire, mais pas copié ou partagé. Remplace auto_ptr, qui est déconseillé. Comparez à boost::scoped_ptr. unique_ptr est petit et efficace ; la taille est un pointeur et prend en charge les références rvalue pour l’insertion et la récupération rapides à partir de collections de bibliothèques standard C++. Fichier d'en-tête : <memory>. Pour plus d’informations, consultez How to : Create and Use unique_ptr Instances and unique_ptr Class.

  • shared_ptr
    Pointeur intelligent de références comptabilisées. À utiliser lorsque vous souhaitez affecter un pointeur brut à plusieurs propriétaires, par exemple, lorsque vous retournez la copie d'un pointeur à partir d'un conteneur, mais que vous souhaitez conserver l'original. Le pointeur brut n'est pas supprimé tant que tous les propriétaires shared_ptr ne sont pas hors de portée ou n'ont pas abandonné la propriété. La taille contient deux pointeurs ; un pour l'objet et un pour le bloc de contrôle partagé qui contient le nombre de références. Fichier d'en-tête : <memory>. Pour plus d’informations, consultez How to : Create and Use shared_ptr Instances et shared_ptr Class.

  • weak_ptr
    Pointeur intelligent spécifique à utiliser en collaboration avec shared_ptr. weak_ptr fournit l'accès à un objet appartenant à une ou plusieurs instances shared_ptr, mais ne participe pas au décompte de références. À utiliser lorsque vous souhaitez observer un objet, mais n'avez pas besoin qu'il demeure actif. Obligatoire dans certains cas pour interrompre les références circulaires entre les instances shared_ptr. Fichier d'en-tête : <memory>. Pour plus d’informations, consultez How to : Create and Use weak_ptr Instances et weak_ptr Class.

Pointeurs intelligents pour les objets COM (programmation Windows classique)

Lorsque vous travaillez avec les objets COM, encapsulez les pointeurs d'interface dans un type de pointeur intelligent approprié. La bibliothèque ATL (Active Template Library) définit plusieurs pointeurs intelligents pour différents besoins. Vous pouvez également utiliser le type pointeur intelligent _com_ptr_t, que le compilateur utilise lorsqu'il crée des classes wrapper à partir de fichiers .tlb. C'est le meilleur choix lorsque vous ne souhaitez pas inclure les fichiers d'en-tête ATL.

CComPtr, classe
Procédez ainsi à moins que vous ne puissiez pas utiliser ATL. Effectue un décompte des références à l'aide des méthodes AddRef et Release. Pour plus d’informations, consultez Guide pratique pour créer et utiliser des instances CComPtr et CComQIPtr.

CComQIPtr, classe
Ressemble à CComPtr, mais fournit également une syntaxe simplifiée pour appeler QueryInterface sur les objets COM. Pour plus d’informations, consultez Guide pratique pour créer et utiliser des instances CComPtr et CComQIPtr.

CComHeapPtr, classe
Pointeur intelligent vers des objets qui utilisent CoTaskMemFree pour libérer de la mémoire.

CComGITPtr, classe
Pointeur intelligent pour les interfaces obtenues à partir de la table GIT (Global Interface Table).

_com_ptr_t, classe
Ressemble à CComQIPtr en termes de fonctionnalités mais ne dépend pas d'en-têtes ATL.

Pointeurs intelligents ATL pour les objets POCO

Outre les pointeurs intelligents pour les objets COM, ATL définit également des pointeurs intelligents et des collections de pointeurs intelligents, pour les anciens objets C++ bruts (POCO). Dans la programmation Windows classique, ces types sont des alternatives utiles aux collections de bibliothèques standard C++, en particulier lorsque la portabilité du code n’est pas requise ou si vous ne souhaitez pas combiner les modèles de programmation de la bibliothèque C++ Standard et ATL.

CAutoPtr, classe
Pointeur intelligent qui applique une propriété unique en transférant la propriété lors de la copie. Comparable à la classe std::auto_ptr déconseillée.

CHeapPtr, classe
Pointeur intelligent pour les objets alloués à l’aide de la fonction C malloc .

CAutoVectorPtr, classe
Pointeur intelligent pour les tableaux alloués à l'aide de new[].

CAutoPtrArray, classe
Classe qui encapsule un tableau d'éléments CAutoPtr.

CAutoPtrList, classe
Classe qui encapsule les méthodes de manipulation d'une liste de nœuds CAutoPtr.

Voir aussi

Pointeurs
Informations de référence sur le langage C++
Bibliothèque C++ standard