web-dev-qa-db-fra.com

Quelle est la signification d'une classe Wrapper C ++?

J'ai un peu de mal à comprendre une classe wrapper. Ce serait formidable si quelqu'un pouvait aider à fournir des exemples appropriés.

  1. Qu'est-ce qu'une classe Wrapper C++ et quelles sont les circonstances de son écriture?
  2. Quelle est son utilisation de quelque façon?

Merci.

38
Mahesh

Une "classe wrapper" est un terme de facto signifiant une classe qui "enveloppe" une ressource; c'est-à-dire qui gère la ressource. Lorsque les gens écrivent un wrapper, ils font quelque chose comme ceci:

class int_ptr_wrapper
{
public:
    int_ptr_wrapper(int value = 0) :
    mInt(new int(value))
    {}

    // note! needs copy-constructor and copy-assignment operator!

    ~int_ptr_wrapper()
    {
        delete mInt;
    }

private:
    int* mInt;
};

Cette classe gère ("enveloppe") un pointeur vers un int. Toutes les ressources doivent être enveloppées d'une certaine manière, pour la propreté (pas de code de nettoyage explicite ou de bruit) et l'exactitude (le destructeur est garanti pour fonctionner; ne peut pas oublier de nettoyer, et sûr à l'exception).

Ce modèle est appelé SBRM (Scoped-bound Resource Management), bien qu'un nom beaucoup plus courant (mais le plus ésotérique) soit Resource-Acquisition is Initialization (RAII). L'idée est de lier le nettoyage d'une ressource à un destructeur, pour les raisons indiquées ci-dessus: la portée gère le reste.

Notez que j'ai dit qu'il manquait un constructeur de copie et un opérateur d'affectation de copie. Cela est dû à la règle de trois . (Voir la question liée pour une explication détaillée.) La manière la plus simple d'implémenter correctement cette règle est d'utiliser l'idiome copy-and-swap, expliqué ici .


Parfois, il n'est pas pragmatique d'écrire une classe wrapper pour le nettoyage des ressources, généralement lorsque la ressource est unique ou utilisée une seule fois. (Ou avec la programmation transactionnelle.) La solution à cela s'appelle scope guard , une façon d'écrire du code de nettoyage à l'intérieur de la fonction qui en a besoin.

Vous pouvez trouver plus d'informations en les recherchant dans votre moteur de recherche préféré (c'est-à-dire Google) ou en accédant au document "principal" ici . Notez que Boost fournit n utilitaire pour cela, comme il le fait généralement pour les bons idiomes.

31
GManNickG

Un wrapper n'est qu'une petite classe dont le but est de fournir une interface différente de celle qu'il enveloppe. Par exemple, il est courant de prendre une API C et d'écrire une ou plusieurs classes qui la "bouclent" pour fournir une interface orientée objet plutôt qu'une interface procédurale.

10
John Zwinck

Vous avez demandé les circonstances de l'écriture de classes wrapper. Par exemple, si vous êtes dans une entreprise qui utilise différents types de caméras, disons USB, FireWire, etc. Chacun des fabricants fournira un ensemble différent de fonctions via une API pour démarrez la caméra, définissez les paramètres et lisez-y le flux d'images.

Désormais, le programmeur qui construit les applications dans votre entreprise doit être isolé de tous les détails spécifiques des différentes API. Maintenant, ce que vous pouvez faire est d'écrire une classe wrapper autour des API pour chacune des caméras ou plus intelligemment, juste une classe avec des fonctions simples, en enroulant le code existant fourni par l'API.

Par exemple, nous pouvons concevoir des classes MyUSBCameraWrapperClass, MyFirewireCameraWrapperClass avec certaines fonctions membres comme setFrameRate (int fps), getImgFrame (* framebuffer), etc.

Les programmeurs de votre entreprise peuvent alors utiliser la caméra usb MyUSBCameraWrapperClass; usbcam.setFrameRate (30), etc. Vous obtenez le point ??

7
hAcKnRoCk

Une classe wrapper est une classe qui encapsule une fonctionnalité avec une autre interface.

Supposons que vous ayez la fonction f():

void f() { std::cout << "hello\n"; }

Une classe wrapper simple peut être

class C {
    f() { std::cout << "hello\n"; }
};

Vous pouvez écrire un wrapper lorsque votre base de code existante attend une interface particulière. C'est l'essence même du modèle de conception de l'adaptateur. Ou vous pouvez encapsuler une fonction dans une classe si vous souhaitez conserver l'état de cette fonction. Ou vous pouvez encapsuler une fonction dans le constructeur ou le destructeur d'une classe si vous voulez qu'elle soit appelée de manière pratique et automatique de manière correcte et déterministe. Et la liste continue.

3
wilhelmtell

J'utilise deux types:

  1. wrappers de ressources pour les paires de fonctions fournies par le système d'exploitation comme

    • UNIX: ouvrir/fermer, mmap/munmap, dlopen/dlclose
    • Windows: CreateFile/DestroyHandle, CreateFileMapping/CloseHandle, LoadLibrary/FreeLibrary
  2. wrappers fonctionnels pour les fonctions fournies par le système d'exploitation comme

    • UNIX: écrire, lire, dlsym
    • Windows: ReadFile, WriteFile, GetProcAddress

L'encapsuleur de ressources s'assure que le code généré par le compilateur s'inquiète de la destruction de la ressource créée par le constructeur via ce qu'on appelle aujourd'hui RAII. Il est facile de combiner de telles classes via des relations de classe base/membre en classes complexes. En cas d'échec de la fonction de création, une exception d'erreur système est levée, fournissant des informations d'erreur détaillées sur l'erreur.

L'encapsuleur fonctionnel est utilisé à la place de la fonction OS simple. En cas d'échec également, une exception système est levée.

De cette façon, quelqu'un qui utilise mon code n'a pas besoin d'un débogueur et d'un code de débogage pour découvrir ce qui échoue dans un environnement complexe avec de nombreuses bibliothèques et processus et machines distantes.

Ces wrappers fournissent également une abstraction du système d'exploitation - le code qui les utilise n'a pas à se soucier des différences de système d'exploitation.

2
user678269