2010-12-14 10 views
3

Angenommen, ich habe diese Klassen:Wie verwaltet man shared_ptr, das auf interne Daten eines bereits referenzierten Objekts zeigt?

struct Engine { 
    int engine_data; 
}; 

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

Aus Performance-Gründen möchte ich in Erinnerung sie dicht gepackt machen (aber ich will nicht die Flexibilität des Designs verlieren). Also, ich kann eine „verpackt“ Struktur erstellen, und eine Fabrik, die transparent eine neue B-Instanz zurück:

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

Das Problem ist, dass diese „Auto“ Instanz wird nie zerstört werden, weil es einen Referenzzähler hat von zwei. Wenn es stirbt, wird es für immer eine Referenzzahl von eins haben. Kennen Sie eine bessere Möglichkeit, die internen shared_ptrs weiter zu verwenden (so dass ich eine "entpackte" Referenz zuweisen kann, wenn ich möchte) und trotzdem diese gepackte Struktur erstellen?

UPDATE

ich einen no-op deleter verwenden könnte, aber dann wäre es sehr gefährlich sein, wenn ich die engine zu halten entscheiden, aber nicht die 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

Warum machst du nicht einfach eine Maschine ein gerades Mitglied des Autos? Wenn Sie immer ein Auto und eine Engine als Teil desselben Objekts erstellen, bedeutet dies, dass sie dieselbe Lebensdauer haben. Auto weiß um Motor, aber nicht umgekehrt. Es sei denn, Sie planen, die gleiche Engine-Instanz zwischen verschiedenen Car-Instanzen zu teilen, würde "struct Car {Engine engine; int Auto_Daten; }; 'ein einfacheres Design sein? –

+0

@Charles Ich habe darüber nachgedacht ... falls ich das Problem nicht einfach mit 'shared_ptr' lösen kann, werde ich auf diese Lösung zurückgreifen. In diesem Beispiel gab ich, ich habe nur eine Komposition, aber in meinem realen Design, habe ich viele Klassen auf diese Weise, in mehreren Schichten, und ich möchte die Flexibilität der Zuweisung von anderen 'Motoren kommen aus anderen Orten . –

Antwort

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

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

Erklärung : Dieser Code erstellt ein shared_ptr, das denkt, dass es die Engine besitzt, aber in der Tat hat es einen separaten Referenzzähler und es tut nichts, wenn der Deleter aufgerufen wird.

+1

Danke! Ich habe darüber schon einmal nachgedacht, aber das könnte einen baumelnden Zeiger erzeugen, der viel schlimmer ist (siehe Post-Update). –

+1

@ e.tadeu: dann denke ich, dass shared_ptr nicht Ihren Bedürfnissen entspricht. Versuchen Sie, Ihren Ansatz neu zu gestalten. Brauchst du wirklich einen Zeiger hier? Warum funktioniert eine reguläre Mitgliedschaft nicht? – ybungalobill

+0

Yup, das ist, was ich denke. Vielleicht würde ich einen "angepassten" shared_ptr oder eine Erweiterung benötigen, um interne Daten abzudecken. Aber wie auch immer, ich habe es hier gepostet, um zu prüfen, ob es eine Lösung gibt, die mir fehlt. :) –

1

Betrachten weak_ptr statt shared_ptr innerhalb struct Car verwenden, es trägt nicht Zählung zu verweisen, sondern kann auf eine shared_ptr umgewandelt werden, wenn nötig.

+0

Danke! Das Problem ist, dass ich oft auf die 'engine' -Instanz zugreifen werde, und' weak_ptr' nicht über 'operator->' verfügt. (Ich will es nicht jedes Mal in ein 'shared_ptr 'konvertieren, wenn ich auf ein Attribut oder eine Methode davon zugreifen möchte). –