2 Stimmen

Bewegungssemantik und Referenzsemantik

Ich schreibe einen baumartigen Container, wo jeder "Knoten" eine Liste mit Verzweigungen/Unterbäumen hat, derzeit sieht mein Kopf so aus:

class _tree {
public:
    typedef _tree* tree_ptr;
    typedef std::list<_tree> _subTreeTy;

    explicit _tree(const _ValTy& v, const _NameTy& n); //create a new tree
    _tree(const _ValTy& v, const _NameTy& n, tree_ptr _root); 
         //create a new tree and add it as branch to "_root".

    ~_tree();

    void add_branch(const _tree& branch); //add by copy
    void add_branch(_tree&& branch); //add by move
private:
    _subTreeTy subtrees;
    _ValTy value;
    _NameTy name;
};

_tree::_tree(const _ValTy& v, const _NameTy& n, tree_ptr _root)
    : root(_root),
    value(v),
    name(n)
{
    _root->add_branch(*this); //not rvalue(???)
}

Nun würde der zweite Konstruktor einen Baum innerhalb von _root - aber wie funktioniert das mit dem Anrufen (Verletzung der Privatsphäre ignorieren):

_tree Base(0,"base");
_tree Branch(1, "branch", &Base);
Base.subtrees.begin()->value = 8;
std::cout << Branch.value;

Wie kann ich es so einrichten, dass Branch & *Base.subtrees.begin() auf denselben Knoten verweisen? Oder sollte ich den anderen Weg gehen. verwenden add_branch() um einen Zweig/Unterbaum zu erstellen?

3voto

Luc Danton Punkte 34153

Bei der Verschiebesemantik geht es darum, die Interna eines Objekts zu verschieben, nicht das Objekt (als typisiertes Stück Speicher) selbst. Es ist am besten, darüber in Form von Werten und Invarianten nachzudenken, da C++ immer noch über eine Wertesemantik verfügt, selbst wenn das Verschieben berücksichtigt wird. Dies bedeutet:

std::unique_ptr<int> first(new int);
// invariant: '*this is either null or pointing to an object'
// current value: first is pointing to some int
assert( first != nullptr );

// move construct from first
std::unique_ptr<int> second(std::move(first));

// first and second are separate objects!
assert( &first != &second );

// New values, invariants still in place
assert( first == nullptr );
assert( second != nullptr );

// this doesn't affect first since it's a separate object
second.reset(new int);

Mit anderen Worten: Sie können den Ausdruck zwar umwandeln *this zu einem rWert, indem Sie std::move(*this) das, was Sie wollen, im Moment nicht erreicht werden kann, da std::list<_tree> verwendet die Wertesemantik und _tree hat selbst eine Wertsemantik. *Base.subtrees.begin() ist ein anderes Objekt als Branch und so wie die Dinge liegen, werden sich Änderungen an ersterem nicht auf letzteres auswirken.

Wechseln Sie zur Referenzsemantik, wenn Sie das wollen (oder brauchen), z. B. durch Verwendung von std::shared_ptr<_tree> y std::enable_shared_from_this (Sie würden dann _root->add_branch(shared_from_this()) innerhalb des Konstruktors). Ich würde das aber nicht empfehlen, das könnte unübersichtlich werden. Value Semantik sind sehr wünschenswert, meiner Meinung nach.


Mit der Wertesemantik könnte die Verwendung Ihres Baums wie folgt aussehen:

_tree Base(0, "base");
auto& Branch = Base.addBranch(1, "branch");

Das heißt, addBranch gibt einen Verweis auf den neu konstruierten Knoten zurück. Einstreuen von etwas Move-Semantik oben drauf:

_tree Base(0, "base");
_tree Tree(1, "branch); // construct a node not connected to Base
auto& Branch = Base.addBranch(std::move(Tree));
// The node we used to construct the branch is a separate object
assert( &Tree != &Branch );

Streng genommen, wenn _tree kopierbar ist, ist die Semantik der Bewegung jedoch nicht erforderlich, Base.addBranch(Tree); würde auch funktionieren.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X