Frage unique_ptr Ownership-Semantik


Vielleicht habe ich versucht, zu allgemein zu sein. (Originalfrage unten) Konkret habe ich eine Abhängigkeit Dep einer Klasse Foo. Ich habe auch eine Klasse MockDep und definiere eine Klasse TestFoo. Hier ist der Konstruktor, den ich zu schreiben versuchte:

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

Und FooDer Konstruktor sieht so aus:

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

mock_dep_ ist delcared in TestFoo wie MockDep* mock_dep_, und dep_ ist in deklariert Foo wie unique_ptr<Dep> dep_. Wie bekomme ich mock_dep_ enthalten dep_Adresse? (wie das oben genannte nicht funktioniert seit std::move(dep) nullt aus dep.)


Ursprünglicher Beitrag:

Ich habe ein Objekt vom Typ Foo dass ich zu einem anderen Objekt des Typs übergehen soll OtherObject die den Besitz beansprucht, aber als Zeiger auf ihre Basisklasse. Ich möchte jedoch einen Zeiger auf das untergeordnete Objekt abrufen, mit dem ich darauf verweisen kann. Ich schrieb etwas wie:

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

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

Dies scheint jedoch nicht zu funktionieren, da die std::move(thing) scheint den Zeiger, von dem zurückgekehrt wird, auf Null zu setzen thing.get() später.

ich kann mich ändern FooDer Parameter ist vom Typ Child* Anstatt von unique_ptr<Child>, aber ich würde es vorziehen, Letzteres zu tun, da es explizit die Eigentümersemantik dokumentiert.

Was ist die am besten geeignete (oder fehlende, unaufdringliche) Art der Lösung dieses Problems?

bearbeiten: Foo und OtherObject Beide sind Klassen, deren Konstrukteure ich definiere.


5
2018-06-09 23:03


Ursprung


Antworten:


Sie können verwenden:

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

Wenn Basisobjekt OtherObject bietet keinen Zugriff auf den Zeiger, Sie können den Konstruktor an einen anderen Konstruktor delegieren, etwa:

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

Eine dritte Lösung wäre, die Reihenfolge zwischen zu ändern OtherObject und child_ (haben child_ vor) durch Einführung einer anderen Ableitung:

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

6
2018-06-09 23:06



In der Regel wird folgendes beschrieben:

§17.6.5.15.1 Vom Status der Bibliothekstypen verschoben [lib.types.movefrom]

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 das Verhalten von std::unique_ptr im:

§ 20.8.1.4 Klassenvorlage unique_ptr [einmalig.tr]

Zusätzlich, u kann auf Anfrage das Eigentum an einen anderen eindeutigen Zeiger übertragen u2. Nach Abschluss einer solchen Übertragung gelten folgende Nachbedingungen:

  • u2.p ist gleich der Vorübertragung u.p,
  • u.p ist gleich nullptr und
  • Wenn der Zustand vor der Übertragung u.d beibehalten wurde, wurde dieser Zustand zu u2.d übertragen.

Speziell, dep, nach dem Bau der Foo Unterobjekt bei:

Foo(std::move(dep))

ist ein nullptr.

Außerdem wenn dep war immer noch ein gültiger Zeiger, Foo(std::move(dep)) hätte kopiert dep, für die es keinen Sinn ergibt std::unique_ptrsemantisch (wie es nicht kopierbar ist).

Was Sie tun möchten, ist eine Referenz auf das spitze Objekt zu geben, mit den gebührenden Erwägungen des Falls (zum Beispiel kann der unique_ptr sein) nullpt? usw ..), 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 konstruiere dann einfach das TestFoo Objekt als:

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

1
2018-06-09 23:32