2010-12-10 41 views
4

boost :: shared_ptr me dérange vraiment. Certes, je comprends l'utilité d'une telle chose, mais je souhaite que je pourrais utiliser le shared_ptr<A>comme un A*. Considérez le code suivantC++: Créer un objet partagé plutôt qu'un pointeur partagé vers un objet

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


class HelpfulContainer 
{ 
public: 
    //Don't worry, I'll manager the memory from here. 
    void eventHorizon(A*& a) 
    { 
     cout << "It's too late to save it now!" << endl; 
     delete a; 
     a = NULL; 
    } 
}; 


int main() 
{ 
    HelpfulContainer helpfulContainer; 

    A* a1 = new A(1); 
    A* a2 = new A(*a1); 
    cout << "*a1 = " << *a1 << endl; 
    cout << "*a2 = " << *a2 << endl; 
    a2->setX(2); 
    cout << "*a1 = " << *a1 << endl; 
    cout << "*a2 = " << *a2 << endl; 
    cout << "Demonstrated here a2 is not connected to a1." << endl; 

    //hey, I wonder what this event horizon function is. 
    helpfulContainer.eventHorizon(a1); 

    cout << "*a1 = " << *a1 << endl;//Bad things happen when running this line. 
} 

Celui qui a créé le HelpfulContainer ne pensait pas à d'autres qui souhaitent conserver des pointeurs vers des objets A. Nous ne pouvons pas donner d'objets helpfulClass boost :: shared_ptr. Mais une chose que nous pourrions faire est d'utiliser l'idiome pimlp pour créer une SharedA qui est lui-même un A:

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

Et la fonction principale peut ressembler à ceci:

int main() 
{ 
    HelpfulContainer helpfulContainer; 

    A* sa1 = new SharedA(new A(1)); 
    A* sa2 = new SharedA(sa1); 
    cout << "*sa1 = " << *sa1 << endl; 
    cout << "*sa2 = " << *sa2 << endl; 
    sa2->setX(2); 
    cout << "*sa1 = " << *sa1 << endl; 
    cout << "*sa2 = " << *sa2 << endl; 
    cout << "this demonstrates that sa2 is a shared version of sa1" << endl; 

    helpfulContainer.eventHorizon(sa1); 
    sa2->setX(3); 
    //cout << "*sa1 = " << *sa1 << endl;//Bad things would happen here 
    cout << "*sa2 = " << *sa2 << endl; 
    //but this line indicates that the originally created A is still safe and intact. 
    //only when we call sa2 goes out of scope will the A be deleted. 
} 

Alors, mon question est la suivante: Est-ce que le motif ci-dessus est un bon motif, ou y at-il quelque chose que je ne considère pas encore. Mon projet actuel a hérité d'une classe HelpfulContainer comme ci-dessus qui supprime les pointeurs dont j'ai besoin, mais j'ai toujours besoin de la structure de données présente dans le HelpfulContainer.


Mise à jour: Cette question est une question subséquente.

+0

Si HelpfulContainer veut prendre en charge le pointeur, il doit utiliser la sémantique correcte pour le faire. L'interface 'eventHorizon' est mal nommée car elle n'explique pas ce qui se passe et le paramètre qu'elle doit indiquer devrait indiquer que la propriété est transférée (disons std :: auto_ptr ou son nouveau remplacement std :: unique_ptr). Ces deux éléments indiquent que l'objet HelpfullContainer prend possession de l'objet et qu'il ne sera plus valide après l'appel. Donc, vous prouvez le point que dans le code C++ peut être mal écrit par des gens qui ne comprennent pas la sémantique du langage. –

Répondre

6

Le point de shared_ptr tout est qu'il (et ses copies) possède l'objet vers lequel il pointe. Si vous voulez donner un A à un conteneur qui gère sa durée de vie, vous ne devriez pas utiliser un shared_ptr car il ne répond pas à vos besoins; HelpfulContainer ne sait comment être le seul propriétaire d'un objet créé dynamiquement, vous devez donc lui donner un pointeur vers un objet qui n'appartient à rien d'autre.

Je pense que c'est habituellement mauvaise conception pour un objet de se soucier de sa propre durée de vie (il existe des exceptions). Il est généralement plus utile si un objet peut faire un travail et que quelque chose d'autre gère sa création et sa déscruction, en choisissant la stratégie de durée de vie la plus simple possible (par exemple, variable locale/automatique).

Si vous devez absolument partager la propriété entre deux choses qui ne coopèrent pas (telles que shared_ptr et HelpfulContainer), vous devrez utiliser une sorte de technique proxy.

Dans ce cas, cependant, il semble que HelpfulContainer n'est pas très utile pour votre situation.

+1

+1: changez simplement ce putain de conteneur (et lisez attentivement le code en l'utilisant), ajouter une surcharge en prenant 'shared_ptr' pourrait être utile, par exemple. –

0

Vous créez donc un Stand-In (SharedA) pour lequel la suppression est correcte. Même si c'est un peu bizarre, je suppose qu'il est nécessaire de travailler avec votre API héritée. Pour améliorer légèrement ceci: Permettre la construction de SharedA d'un shared_ptr, mais pas l'inverse - et seulement utiliser le SharedP lorsque vous devez absolument:

int main() 
{ 
    HelpfulContainer helpfulContainer; 

    boost::shared_ptr<A> sa1(new A(1)); 

    // deletes its parameter, but that's okay 
    helpfulContainer.eventHorizon(new SharedA(sa1)); 
} 
1

Je ne suis pas sûr de ce que cela fait pour vous.

Si helpfulContainer.eventHorizon()toujours supprime son paramètre, alors pourquoi passer pas seulement une nouvelle copie (l'original) Une classe:

helpfulContainer.eventHorizon(new A(sa1)); 

Ou, si helpfulContainer.eventHorizon()parfois seulement supprime son paramètre, puis faire l'appel comme

helpfulContainer.eventHorizon(new SharedA(sa1)); 

fuira deux la SharedA et l'original A (s a1) dans les cas où il choisit de ne pas supprimer.

+0

Dans ce cas, eventHorizon ne supprime pas le pointeur, mais le stocke dans une structure de données où. Si les shared_ptrs associés sont tous supprimés, un pointeur invalide se cache dans la structure de données. – JnBrymn

+0

Si vous savez que eventHorizon va s'approprier le pointeur, et le supprimera finalement, et que vous devez garder la propriété pour vous-même - alors passez une nouvelle copie, comme dans le premier formulaire que j'ai montré ('helpfulContainer.eventHorizon (nouveau A (sa1)); –

+0

@ John Berryman: Peut-être que j'ai mal interprété votre commentaire. Pour votre première phrase: Si eventHorizon contient le pointeur, et le supprime à loisir, et que vous avez toujours besoin d'accéder à ce même objet, alors votre proposition ** pourrait ** aider. Mais vous devrez refactoriser votre code de façon très précise afin que les pointeurs sur les objets 'A' bruts ne soient jamais utilisés, sauf via un objet' SharedA'. Toute tentative de mélanger les deux conduirait à beaucoup de gémissements et de grincements de dents. Je pense que vous aurez également besoin d'un constructeur de copie spécialisé pour SharedA (const SharedA &) qui copie 'mImpl'. –

0

Les conversions implicites au type de pointeur sous-jacent sont incompatibles avec l'utilisation prévue de shared_ptr en ce que vous pouvez facilement passer le shared_ptr à une fonction, etc. sans le savoir.

Il me semble que HelpfulContainer est quelque chose mais utile et devrait être réparé ou abandonné.

Si ce n'est pas possible alors probablement le meilleur moyen est de simplement copier le A que vous voulez passer et transmettre la copie au conteneur.