2

J'ai une classe fourni à partir d'une bibliothèque comme ceci:coulée entre deux classes de type-basé sur un modèle utilisant des pointeurs Partagés

template <typename T> 
class TypedClass 
{ 
public: 
    typedef typename boost::shared_ptr<TypedClass<T> > Ptr; 

    T m_data; 
    T* m_pointer_data; 
}; 

En supposant que je suis prêt à accepter que int et float sont toujours la même taille (et l'alignement) sur cette architecture particulière, cela me semble valable:

TypedClass<int>* int_object = new TypedClass<int>(); 

TypedClass<float>* float_object = reinterpret_cast<TypedClass<float>* >(int_object); 

maintenant, je suis en train de réaliser la même chose en utilisant shared_ptrs boost et je suis venu à ceci:

TypedClass<int>::Ptr int_object = TypedClass<int>::Ptr(new TypedClass<int>()); 

void* int_object_void_pointer = reinterpret_cast<void*>(int_object.get()); 

TypedClass<float>::Ptr float_object(reinterpret_cast<TypedClass<float>*>(int_object_void_pointer)); 

Ce qui semble fonctionner très bien, mais cette utilisation de pointeurs partagés fera l'objet à supprimer deux fois que je voudrais éviter. Il est important de noter que 'TypedClass' fait partie d'une bibliothèque tierce et que cette bibliothèque utilise des pointeurs partagés pour toutes ses fonctionnalités internes. J'ai donc besoin des données dans ce formulaire. J'ai déjà résolu ce problème en héritant de boost enable_shared_from_this, mais ce n'est pas possible ici.

Il s'agit d'une technique simple permettant de réutiliser le même objet pour des types de données de même taille sans avoir à allouer un nouvel objet au nouveau type.

Suggestions bienvenues.

+1

Je crois qu'il y a un problème que vous obtiendrez deux pointeurs partagés indépendants sur la même mémoire. Si le nombre de références d'une instance tombe à zéro, le pointeur est supprimé alors que l'autre reste valide, mais pointe toujours vers des données non définies. – Erbureth

+0

"En supposant que je suis prêt à accepter que int et float sont toujours de la même taille sur cette architecture particulière" également le même alignement? Même si vous êtes assuré par votre compilateur (pour votre architecture) que le cast 'reinterpret_cast *> (int_object);' et les opérations sur le résultat produiront un comportement attendu, vous produisez toujours UB comme Erbureth le signale (votre objet sera supprimé deux fois, une fois par chaque ptr partagé "int_obj" et "float_obj"). – dyp

+0

J'ai résolu ce problème par le passé en héritant du boost enable_shared_from_this, mais comme c'est une bibliothèque tierce qui n'est pas vraiment une option ici. Je ne connais pas d'autre moyen de le faire. (Mise à jour de la question avec cette information). – Dan

Répondre

6

shared_ptr<T> a un constructeur intéressant surchargé:

template<class Y> shared_ptr(shared_ptr<Y> const & r, element_type * p); 

En gros, cela construit une shared_ptr qui prend la Deleter et la refcounting de r, sauf qu'il détient p.

Vous pouvez l'utiliser comme ceci:

TypedClass<int>::Ptr int_object = TypedClass<int>::Ptr(new TypedClass<int>()); 

TypedClass<float>::Ptr float_object(int_object,reinterpret_cast<TypedClass<float>*>(int_object.get())); 

EDIT:

Si vous utilisez Boost> = 1.53.0, il y a aussi boost::reinterpret_pointer_cast. Donc, vous pouvez écrire:

TypedClass<float>::Ptr float_object = boost::reinterpret_pointer_cast<TypedClass<float> >(int_object); 
+0

Bon à savoir ce constructeur intéressant et reinterpret_pointer_cast. Merci! – Mine

+0

Remarque: La classe TypedClass * dans l'alias de pointeur partagé n'est jamais supprimée. Lorsque l'alias est hors de portée et qu'il contient la dernière référence, TypedClass * sera supprimé. Vous pourriez regarder (http://www.codesynthesis.com/~boris/blog/2012/04/25/shared-ptr-aliasing-constructor) +1 –

0

Je pense que vous ne pouvez pas, sauf si vous surchargez la classe partagée ptr elle-même avec deux paramètres typename, car il conserve la référence aux données et supprime lorsque le compte est 0. Mais comme vous devez passer d'un type à un autre, le boost partagé ptr, pensera que vous avez publié les données de toute façon.

shared_ptr p = ptr; // ajoute une référence si ptr et p sont du même type.

si le type est pas le même que vous récupérez les données internes puis relâchez.

une autre solution peut être d'avoir toutes les données pour tenir dans ce conteneur en utilisant boost :: tout.

1

Si vous avez vraiment tentative de réutiliser le même objet pour les types de données qui ont la même taille sans avoir à allouer un nouvel objet avec le nouveau type de bibliothèque tierce, vous avez le choix limité:

  1. Vous ne devez pas allouer shared_ptr du pointeur brut, sinon il est supprimé deux fois, provoquant une erreur de segment;
  2. Vous réutilisez le type (shared_ptr) de sorte que vous pouvez directement "copier" par operator =(); ou chat le pointeur brut et assurez-vous que vous ne faites pas de changements qui affectent l'allocation/suppression de la mémoire.

Comme votre exemple, je suggère le code comme ci-dessous:

float* float_ptr = reinterpret_cast<float*>(&int_object->m_data); 
// Do something with *float_ptr 
// And never delete it! 
1

Vous pouvez utiliser boost pointer cast. C'est une solution très laide, mais au moins le comptage ref fonctionnera de cette façon.

TypedClass<int>::Ptr int_object = TypedClass<int>::Ptr(new TypedClass<int>()); 
TypedClass<float>::Ptr float_object = boost::static_pointer_cast<TypedClass<float>>(boost::shared_ptr<void>(int_object)); 
0

Si TypedClass est attribué sur votre code (et non dans la bibliothèque externe), vous pouvez utiliser destructor spécifique pour éviter la destruction multiple:

template<class T> 
struct NullDestructor 
{ 
    void operator()(TypedClass<T> *& t) { /* nothing to do */ } 
}; 

template<class T> 
typename TypedClass<T>::Ptr make_fake_shared_ptr(TypedClass<T> * ptr) 
{ 
    return typename TypedClass<T>::Ptr(ptr, NullDestructor<T>()); 
} 


TypedClass<int>::Ptr int_object = make_fake_shared_ptr<int>(new TypedClass<int>()); 

TypedClass<float> * ptr = reinterpret_cast<TypedClass<float>*>(int_object.get()); 
TypedClass<float>::Ptr float_object = make_fake_shared_ptr<float>(ptr); 

Avec cette solution, vous êtes en charge de la destruction manuelle de la mémoire à la fin:

delete float_object.get(); 

Vous pouvez améliorer cette solution en utilisant un allocateur personnalisé et un pool.

Questions connexes