C ++: Création d’un object partagé basé sur un modèle plutôt que d’un object shared_ptr

D’après ma question précédente, j’aimerais que boost::shared_ptr soit en réalité une sous-classe de A (ou peut-être A* ) pour pouvoir être utilisée dans des méthodes prenant A* comme argument.

Considérez la classe suivante:

 class A { public: A(int x) {mX = x;} virtual void setX(int x) {mX = x;} virtual int getX() const {return mX;} private: int mX; }; 

Dans la question précédente, j’ai proposé la création d’un object SharedA pour résoudre ce problème, et c’est probablement le cas.

 class SharedA : public A { public: SharedA(A* a) : mImpl(a){} virtual void setX(int x) {mImpl->setX(x);} virtual int getX() const {return mImpl->getX();} private: boost::shared_ptr mImpl; }; 

Ce serait une pensée pour Grrrrrrrreat, si je pouvais créer une classe de modèle pour gérer tout cela pour moi.

 template  class Shared : public T { public: SharedT(T* t) : mImpl(t) { //What kind of crazy voodoo goes here? } private: boost::shared_ptr mImpl; }; 

Si j’avais cela (avec les constructeurs appropriés dans Shared ), alors je pourrais faire ce qui suit:

 A* indestructo_A = new Shared(new A(100)); A* indestructo_A_clone = new Shared(indestructo_A); delete indestructo_A cout << "Indestructo-A back with a vengence!" << indestructo_A_clone.getX(); 

Des questions:

  1. Est-ce utile? Ou son utilité n’est-elle utile que dans les cas où vous avez affaire à un code particulièrement défectueux. Par exemple:

    void aFunctionYouHaveToUse (A * a) {/ un algorithme utile puis /
    supprimer un; }

  2. Est-il possible de construire une telle classe basée sur un modèle? (Je suppose que vous avez besoin de reflection, non?) Si vous pouvez le construire, comment?

Shared_ptr ne permet pas le transtypage explicite en A * pour une raison diablement bonne (il existe de meilleures façons de le faire que d’hériter, ce qui serait impossible de toute façon). Shared_ptr, ainsi que d’autres pointeurs intelligents, ont pour seul objective de fournir un petit object encapsulé dont le seul objective est de posséder un pointeur et de décider quand et comment le supprimer.

Si cet object permettait à ce même pointeur de se faire passer sournoisement sans réfléchir, il ne pourrait tout simplement pas remplir sa fonction. Chaque fois que vous creusez dans un pointeur intelligent pour obtenir le pointeur brut à l’intérieur, vous enfreignez sa sémantique de propriété et vous devez alors faire très attention de ne pas faire une bêtise. Les pointeurs avisés vous incitent à y penser en vous forçant à passer un appel pour accéder au pointeur à l’intérieur plutôt que de le faire en silence chaque fois que vous le passez accidentellement à un type de fonction incorrect. Pensez-y comme si le pointeur intelligent disait: “Hé, vous savez que ce que vous faites peut être dangereux, non? OK, alors voilà.”

IMHO, l’univers serait un meilleur endroit si les pointeurs partagés ne permettaient pas l’access à leurs pointeurs. Malheureusement, ce n’est pas cet univers et ce ne peut pas être cet univers, car il est parfois nécessaire de transférer cette chose dans une fonction qui n’utilise pas de pointeur intelligent. Donc, comme nous ne vivons pas dans ce meilleur univers, NOS pointeurs intelligents autorisent l’access, ils ne sont tout simplement pas salauds à ce sujet.

Peut-être y a-t-il un moyen, mais c’est un peu sale. Vous pouvez essayer de mettre une autre classe dans votre hiérarchie: ABase. A pourrait hériter d’ABase et ABase contiendrait les implémentations par défaut de toutes les méthodes publiques. Quelque chose comme ça:

 #include  #include  class ABase { public: ABase() {} void init( ABase *a ) { mImpl.reset( a ); } virtual void setX( int x ) { mImpl->setX( x ); } virtual int getX() const { return mImpl->getX(); } boost::shared_ptr< ABase > mImpl; }; class A : public ABase { public: typedef ABase BaseClass; A(int x) {mX = x;} virtual void setX(int x) {mX = x;} virtual int getX() const {return mX;} private: int mX; }; template  class Shared : public T::BaseClass { public: Shared(T* t) { init( t ); } }; int main() { Shared< A > sh( new A( 1 ) ); std::cout << sh.getX() << std::endl; sh.setX( 5 ); std::cout << sh.getX() << std::endl; } 

Ceci n'est qu'un concept. J'ai besoin de vérifier le code. L’idée principale est de passer de l’implémentation par défaut en héritant d’un partage non directement du type A, mais de son type de base, qui a toutes les implémentations nécessaires. La classe de base a besoin de plus de travail, mais je pense que l'idée est assez claire.

Ce n'est pas exactement ce que vous avez demandé (c'est en fait impossible), mais cela pourrait être utile dans certains cas, et c'est probablement le plus proche possible.

Mettre de côté si c’est une bonne idée ou non. Si vous souhaitez un type de pointeur partagé pouvant être implicitement converti en un pointeur brut, vous n’avez pas besoin d’hériter du type pointee ni de subir d’autres complications de ce type. Créez simplement un type de pointeur partagé avec les opérateurs de conversion implicites appropriés.

Cela peut se faire par inheritance ou composition d’un type de pointeur partagé existant. (Ou bien en faisant votre propre pointeur partagé à partir de zéro).

Utiliser l’inheritance, par exemple:

 template class convertible_shared_ptr : public boost::shared_ptr { public: convertible_shared_ptr() {} convertible_shared_ptr(T* ptr) : boost::shared_ptr(ptr) {} operator T* () const { return get(); } T* operator-> () const { return get(); } }; 

Vous utilisez alors une chose comme ceci:

 #include  #include  using namespace std; class A // simple class for example purposes { public: A() : member(42) {} int member; }; void foo(A* a) { cout << "a->member=" << a->member << endl; } int main() { convertible_shared_ptr ptr(new A); foo(ptr); cout << "ptr->member=" << ptr->member << endl; return 0; } 

Bien sûr, je n'ai pas réellement essayé une telle chose dans un scénario réel. Il peut y avoir certaines complications lorsqu’on essaie d’interagir avec les types weak_ptr . À tout le moins, il pourrait y avoir un peu plus de code nécessaire pour couvrir tous les usages possibles.