2010-12-14 100 views
3

Supposons que j'ai ces classes:Comment gérer shared_ptr qui pointe vers les données internes d'un objet déjà référencé?

struct Engine { 
    int engine_data; 
}; 

struct Car { 
    shared_ptr<Engine> engine; 
    int car_data; 
}; 

Pour des raisons de performance, je veux les rendre bien emballés dans la mémoire (mais je ne veux pas perdre la flexibilité de la conception). Donc, je peux créer une structure « tassée », et une usine qui sera de retour en toute transparence une nouvelle instance B:

struct PackedCarAndEngine { 
    Engine engine; 
    Car car; 
}; 

shared_ptr<Car> new_Car() { 
    shared_ptr<PackedCarAndEngine> packed = make_shared<PackedCarAndEngine>(); 

    // uses aliasing shared_ptr constructor 
    packed->car.engine = shared_ptr<Engine>(packed, &packed->engine); 

    // again 
    shared_ptr<Car> car = shared_ptr<Car>(packed, &packed->car); 

    return car; 
} 

Le problème est que cette instance « voiture » ne sera jamais détruit, parce qu'il a un compte de référence de deux. Quand il meurt, il aura un compte de référence d'un, pour toujours. Savez-vous un meilleur moyen de continuer à utiliser le shared_ptr interne (de sorte que je peux attribuer une référence "déballé" si je veux), et encore faire cette structure emballée?

MISE À JOUR

je pouvais utiliser un no-op Deleter, mais il serait très dangereux si je décide de garder le engine mais pas le car:

// ... 
    packed->car.engine = shared_ptr<Engine>(&packed->engine, do_nothing_deleter); 
    // ... 

shared_ptr<Car> my_car = new_Car(); 
shared_ptr<Engine> my_engine = my_car->engine; 
my_car.reset(); // Danger: engine was destroyed here!!! 
cout << my_engine->engine_data; // Crash! 
+4

Pourquoi ne faites-vous pas d'un moteur un membre droit de Car? Si vous créez toujours une voiture et un moteur dans le même objet, cela signifie qu'ils ont la même durée de vie. La voiture connaît le moteur mais pas l'inverse. Sauf si vous envisagez de partager la même instance de moteur entre différentes instances de Car ne serait pas 'struct Car {engine Engine; int car_data; }; 'être un design plus simple? –

+0

@Charles J'ai réfléchi à cela ... au cas où je ne pourrais pas résoudre le problème facilement avec 'shared_ptr', je reviendrai sur cette solution. Dans cet exemple, j'ai donné, je n'ai qu'une seule composition, mais dans mon vrai design, j'ai beaucoup de classes composées de cette façon, en plusieurs couches, et je veux avoir la flexibilité d'assigner différents moteurs provenant d'autres endroits . –

Répondre

0
void nop(Engine*) { /* do nothing */ } 

packed->car.engine = shared_ptr<Engine>(&packed->engine, nop); 

Explication : Ce code crée un shared_ptr qui pense qu'il possède le moteur mais en fait il a un compteur de référence séparé ET il ne fait rien lorsque le suppresseur est appelé.

+1

Merci! J'ai déjà pensé à cela avant, mais cela peut créer un pointeur qui se balance, ce qui est bien pire (voir la mise à jour post). –

+1

@ e.tadeu: alors je pense que shared_ptr ne correspond pas à vos besoins. Essayez de repenser votre approche. Avez-vous vraiment besoin d'un pointeur ici? Pourquoi une adhésion régulière ne fonctionne pas? – ybungalobill

+0

Ouais, c'est ce que je pense. Peut-être aurais-je besoin d'un shared_ptr "personnalisé", ou d'une extension pour couvrir les données internes. Mais, de toute façon, je l'ai posté ici pour vérifier s'il y a une solution qui me manque. :) –

1

Pensez à utiliser weak_ptr au lieu de l'intérieur shared_ptrstruct Car, il ne contribue pas à faire référence à nombre, mais peut être converti en un shared_ptr en cas de besoin.

+0

Merci! Le problème est que j'accéderai beaucoup à l'instance 'engine', et' weak_ptr' n'a pas 'operator- >'. (Je ne veux pas le convertir en 'shared_ptr' chaque fois que je veux accéder à un attribut ou une méthode de celui-ci). –