2014-06-09 15 views
5

Vielleicht habe ich versucht, zu allgemein zu sein. (Original Frage unten) Konkret habe ich eine Abhängigkeit Dep einer Klasse Foo. Ich habe auch eine Klasse MockDep und definiere eine Klasse TestFoo. Hier ist Konstruktor Ich habe versucht zu schreiben:unique_ptr Besitz Semantik

TestFoo(unique_ptr<MockDep> dep) : Foo(std::move(dep)), mock_dep_(dep.get()) {} 

Und Foo ‚s Konstruktor wie folgt aussieht:

Foo(unique_ptr<Dep> dep) : dep_(dep) {} 

mock_dep_ in TestFoo als MockDep* mock_dep_ delcared ist und dep_ wird in Foo als unique_ptr<Dep> dep_ erklärt. Wie kann ich mock_dep_ erhalten, um die Adresse dep_ zu enthalten? (Wie die oben nicht funktioniert, da std::move(dep) nulls aus dep.)


Original-Beitrag:

Ich habe ein Objekt vom Typ Foo, dass ich zu einem anderen Objekt des Typs passieren OtherObject, die Eigentümerschaft davon beansprucht, aber als Zeiger auf seine Basisklasse. Ich möchte jedoch einen Zeiger auf das untergeordnete Objekt abrufen, mit dem ich darauf verweisen kann. Ich schrieb so etwas wie:

Foo(std::unique_ptr<Child> thing) : 
    OtherObject(std::move(thing)), child_(thing.get()) {} 

OtherObject(std::unique_ptr<Base> thing, ...) { ... } 

Allerdings bedeutet dies nicht zu funktionieren scheint, wie der std::move(thing) den Zeiger auf Null zu bringen scheint, die später von thing.get() zurückgibt.

Ich kann Foo 's Parameter ändern, um vom Typ Child* anstelle von unique_ptr<Child> zu sein, aber ich würde es vorziehen, Letzteres zu tun, da es die Eigentumssemantik ausdrücklich dokumentiert.

Was ist die am besten geeignete (oder fehlschlagende, unaufdringliche) Möglichkeit, dies zu lösen?

bearbeiten: Foo und OtherObject sollen beide Klassen sein, deren Konstrukteure ich definieren.

+1

Was ist 'OtherObject'? AFAIK kann es in diesem Zusammenhang keine Funktion sein. – Shoe

+0

@Jefffrey mein schlechter, nachbearbeiteter – Alec

+0

@ Jeffrey Ich bin mir nicht sicher, welche Verwirrung bleibt - ich schrieb den Beitrag um, um direkter auf mein Problem zu verweisen. – Alec

Antwort

6

Sie verwenden können:

Foo(std::unique_ptr<Child> thing) : 
    OtherObject(std::move(thing)), 
    child_(OtherObject.getChildPtr()) /* one accessor to get the pointer. */ 
{} 

Wenn Basisobjekt OtherObject nicht einen Accessor auf den Zeiger liefern, können Sie den Konstruktor einen anderen Konstruktor delegieren, so etwas wie:

class Foo: public OtherObject 
{ 
public: 
    Foo(std::unique_ptr<Child> thing) : Foo(thing, thing.get()) {} 

private: 
    Foo(std::unique_ptr<Child>& thing, Child* child) : 
     OtherObject(std::move(thing)), 
     child_(child) 
    {} 
private: 
    Child* child_; 
}; 

A Die dritte Lösung wäre, die Reihenfolge zwischen OtherObject und child_ (vorher child_) zu ändern, indem eine andere Ableitung eingeführt wird:

class ChildPtr 
{ 
public: 
    ChildPtr(Child* child) : child_(child) {} 

    Child* child_; 
}; 

class Foo: private ChildPtr, public OtherObject 
{ 
public: 
    Foo(std::unique_ptr<Child> thing) : 
     ChildPtr(thing.get()), 
     OtherObject(std::move(thing)) 
    {} 
}; 
+1

'OtherObject' ist eine Basisklasse entsprechend zu OP. – Jarod42

+0

'OtherObject' soll ein beliebiger Typ sein, kein' unique_ptr'. – Alec

+0

Unter Verwendung der Terminologie meines editierten Posts wollte ich vermeiden, 'Foo' zu modifizieren, um einen Getter bereitzustellen (hauptsächlich, weil es einen statischen_cast erfordern würde), aber die andere Lösung ist auch nicht zu attraktiv, also könnte ich einfach mit dem gehen Casting-Accessor. – Alec

1

Im Allgemeinen, was geschieht, ist in der Norm beschrieben als:

§17.6.5.15.1 fahrener vom Zustand der Bibliothekstypen [lib.types.movedfrom]

Objekte von Typen, die in der C++ - Standardbibliothek definiert sind, können von (12.8) verschoben werden. Verschiebeoperationen können explizit angegeben oder implizit generiert werden. Sofern nicht anders angegeben, müssen solche verschobenen Objekte in einen gültigen, jedoch nicht spezifizierten Zustand versetzt werden.

Der Standard beschreibt tatsächlich speziell das Verhalten von std::unique_ptr in:

§20.8.1.4 Vorlage Klasse unique_ptr [unique.ptr]

, zusätzlich u können auf Wunsch Transfer Besitz zu einem anderen einzigartigen Zeiger u2. Nach Abschluss einer solchen Übertragung die folgenden Nachbedingungen halten:

  • u2.p zum Vorübertragungs-up gleich ist,
  • bis nullptr gleich ist, und
  • wenn die Pre-Transfer ud beibehalten Zustand, dieser Zustand wurde auf u2.d übertragen.

Insbesondere dep, nach dem Bau des Foo Subobjekt auf:

Foo(std::move(dep)) 

ist ein nullptr.

Außerdem, wenn dep war immer noch ein gültiger Zeiger, würde Foo(std::move(dep))dep kopiert haben, die für die semantische std::unique_ptr ‚s keinen Sinn macht (wie es nicht kopierbaren).

Was Sie tun möchten, ist ein Verweis auf die spitzen Gegenstand zu lassen, mit den durch Erwägungen des Gehäuses (zum Beispiel kann der unique_ptr sein nullpt etc ..?), In Foo:

class Foo { 
public: 
    Foo(unique_ptr<Dep> dep) : dep_(dep) {} 
    const Dep& get_dep() const { return *dep_; } 
    Dep& get_dep()    { return *dep_; } 
private: 
    std::unique_ptr<Dep> dep_; 
}; 

und dann konstruiere einfach das TestFoo Objekt als:

TestFoo(unique_ptr<MockDep> dep) : Foo(std::move(dep)) {} 
+0

Ich glaube, 'unique_ptr' garantiert zwar, dass es null ist? – Mehrdad