2010-12-10 42 views
2

par mon précédent question, je souhaite que boost::shared_ptr<A> était en fait une sous-classe de A (ou peut-être A*) afin qu'il puisse être utilisé dans les méthodes qui ont A* comme leur argument.C++: Création d'un objet partagé <T> basé sur un modèle plutôt qu'un shared_ptr <T> objet

Tenir compte de 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 objet SharedA pour prendre soin de cela, et on peut supposer qu'il fait.

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<A> mImpl; 
}; 

Il serait pensée Grrrrrrrreat, si je pouvais créer une classe de modèle pour prendre soin de tout cela pour moi.

template <class T> 
class Shared : public T 
{ 
public: 
    SharedT(T* t) : mImpl(t) 
    { 
    //What kind of crazy voodoo goes here? 
    } 

private: 
    boost::shared_ptr<T> mImpl; 
}; 

Si je devais cela, (ainsi que les constructeurs appropriés dans Shared<T>), alors que je pouvais faire ce qui suit:

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

Questions:

  1. Est-ce utile ? Ou est son utilité seulement dans les cas de coin où vous avez affaire à un code particulièrement mauvais. Par exemple:

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

  2. Est-il possible de construire une telle classe de modèles? (Je suppose que vous avez besoin de réflexion, non?) Si vous pouvez le construire, comment?

Répondre

8

Il y a une très bonne raison pour laquelle shared_ptr n'autorise pas le casting explicite vers A * (il y a de meilleures façons de le faire que d'hériter, ce qui serait impossible de toute façon). Le but de shared_ptr, et d'autres pointeurs intelligents, est de fournir un petit objet encapsulé dont le seul but est de posséder un pointeur et de décider quand et comment le supprimer.

Si cet objet permettait à ce même pointeur de se contenter de se tromper, sans réfléchir, il ne pourrait tout simplement pas remplir son rôle.Chaque fois que vous creusez dans un pointeur intelligent pour obtenir le pointeur brut à l'intérieur vous violez sa sémantique de propriété et devez alors faire très attention à ne pas faire quelque chose de stupide. Les pointeurs intelligents vous font penser à cela en vous forçant à faire un appel pour accéder au pointeur à l'intérieur plutôt que de le faire silencieusement chaque fois que vous le passez accidentellement au mauvais type de fonction. Pensez-y comme le pointeur intelligent disant, "Hey, vous savez que ce que vous faites peut être dangereux, non?" Alors, vous voilà. " À mon humble avis, l'univers serait un meilleur endroit si les pointeurs partagés ne permettaient pas d'accéder à leurs pointeurs. Malheureusement, ce n'est pas cet univers et ne peut pas être cet univers parce que parfois vous devez toujours passer cette chose dans une fonction qui n'utilise pas de pointeurs intelligents. Donc, puisque nous ne vivons pas dans cet univers meilleur, nos pointeurs intelligents permettent l'accès, ils ne sont tout simplement pas des salopes à ce sujet.

+0

À mon humble avis, je dois dire de bien des façons que je suis d'accord avec cela, mais à bien des égards, je ne suis pas d'accord. Il y a beaucoup de fois où l'on a, disons, un conteneur de pointeurs intelligents, et veut avoir un pointeur brut qui traîne à quelque chose à l'intérieur du conteneur. Bien sûr, le conteneur est toujours le propriétaire logique de l'objet, et l'autre code doit être conscient de cela, mais il vous permet d'avoir la sécurité exception faite par le pointeur intelligent mais pas de gérer les comptes de référence, ni autoriser d'autres codes à voler la propriété et empêcher le shared_ptr d'être nuked. –

+0

Je suis confus. Avec quoi êtes-vous en désaccord exactement? –

+0

"À mon humble avis, l'univers serait un meilleur endroit si les pointeurs partagés ne permettaient pas l'accès à leurs pointeurs." –

1

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

#include <iostream> 
#include <boost/shared_ptr.hpp> 

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 T> 
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 est seulement un concept. J'ai besoin de vérifier le code. L'idée clé est de passer l'implémentation par défaut en héritant non pas 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 il pourrait être utile dans certains cas, et est probablement le plus proche que vous pouvez obtenir.

+1

Les tortues tout le long ... –

+0

Oui, mais créer une autre classe dans la hiérarchie est la chose même que j'essaie d'éviter. Ce serait bien de créer une classe 'A' et ensuite d'utiliser' A * = new Shared (new A()) 'sans avoir à faire de travail supplémentaire. – JnBrymn

+0

Oui, mais c'est impossible. Si vous construisez une classe de base pour toute la nouvelle hiérarchie de classes, cela pourrait être utile à long terme, car vous ne devriez le faire qu'une seule fois pour chaque hiérarchie, mais la solution est rigide et seulement un concept (comme je l'ai déjà fait). souligné). Une autre façon de voir ce problème est que vous essayez essentiellement de mettre en œuvre un modèle de conception composite sans interface de base. Malheureusement, ce n'est pas pris en charge dans la langue, qui manque déjà de réflexion, ce qui serait d'une utilité limitée dans votre cas. – LavaScornedOven

0

Mettre de côté si c'est une bonne idée. Si vous voulez un type de pointeur partagé qui peut être implicitement converti en un pointeur brut, vous n'avez pas besoin d'hériter du type de pointe ni d'autres complications de ce type. Il suffit de créer un type de pointeur partagé avec les opérateurs de conversion implicites appropriés.

Cela peut être effectué via l'héritage ou la composition d'un type de pointeur partagé existant. (Ou bien en faisant votre propre pointeur partagé à partir de zéro).

Utilisation de l'héritage, par exemple:

template<typename T> 
class convertible_shared_ptr : public boost::shared_ptr<T> 
{ 
public: 
    convertible_shared_ptr() {} 
    convertible_shared_ptr(T* ptr) : boost::shared_ptr<T>(ptr) {} 

    operator T*() const 
    { 
     return get(); 
    } 

    T* operator->() const 
    { 
     return get(); 
    } 
}; 

Vous utilisez alors une telle chose comme ceci:

#include <iostream> 
#include <boost/shared_ptr.hpp> 
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<A> 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 monde réel . Il peut y avoir quelques complications lorsque vous essayez d'interagir avec les types weak_ptr correspondants. À tout le moins, il pourrait y avoir plus de code nécessaire pour couvrir tous les usages possibles.