Copy Constructor: Vererbung & dynamische Bindung



  • Tut mir leid, dass ich ein altes Thema wieder einmal aufgreife. Denke mal Copy Constructoren sind hier schon sehr oft behandelt worden, hab aber trotzdem eine Frage, bei der ich hilfe brauche!

    Also, folgendes (binge hier nur den noetigen Quellcode - also die Copy Constructoren - hoffe dar reicht, sonst reich ich den Rest noch nach):

    Node::Node(const Node &node) :
    	left(0), right(0) {
    	if (node.left != 0)
    		this->left = new Node(*(node.left));
    	if (node.right != 0)
    		this->right = new Node(*(node.right));
    }
    

    und die abgeleitete Klasse (public):

    FSNode::FSNode(const FSNode &fsnode) :
    	Node(fsnode), name(fsnode.name) {
    }
    

    Problemstellung:
    left und right sind Elemente vom Typ Node, das heisst es koennen auch abgeleitete Klassen dort verwendet werden - sprich FSNode.
    Wenn ich jetzt folgendes mache:

    FSNode *fsn1 = new FSNode("FSNode #1");
    	fsn1->SetLeft(new FSNode("FSNode left"));
    	fsn1->SetRight(new FSNode("FSNode right"));
    
    	FSNode *fsn2 = new FSNode(*fsn1);
    	// delete spar ich mir hier auf
    

    sind bei fsn1 left und right zwar laut dynamischer Bindung Objekte vom Typ
    FSNode, dies wird aber vom Kopierkonstructor nicht beruecksichtigt und ich erhalte bei fsn2 nur Elemente vom Typ Node.
    Warum das so ist mir klar, hab auch das Konzept dahinter verstanden (jedenfalls ohne Ableitung).
    Aber ich habe keinen wirklichen Plan, wie man das hier loesen soll ...
    Von FSNode leiten naehmlich noch mal zwei Klassen ab, das ganze ist also relativ kompliziert schon ....

    Ich hoffe natuerlich wie immer dass die Problemerklaerung halbwegs verstaendlich ist, falls es noch Erlaeuterungen braucht nur melden.
    Aber ich bin mir sicher viele von euch kennen das Problem und koennen mir auch demensprechend weiterhelfen!

    Danke schon mal im voraus!



  • Da wirst du wohl eine virtuelle clone() Methode implementieren müssen:

    class Node
    {
    public;
       virtual ~Node();
       virtual Node* clone() const = 0;
    };
    
    class FSNode
    {
    public;
       Node* clone() const
       {
          return new FSNode( *this );
       }
    };
    


  • moe szyslak schrieb:

    Wenn ich jetzt folgendes mache:

    FSNode *fsn1 = new FSNode("FSNode #1");
    	fsn1->SetLeft(new FSNode("FSNode left"));
    	fsn1->SetRight(new FSNode("FSNode right"));
    
    	FSNode *fsn2 = new FSNode(*fsn1);
    	// delete spar ich mir hier auf
    

    sind bei fsn1 left und right zwar laut dynamischer Bindung Objekte vom Typ
    FSNode, dies wird aber vom Kopierkonstructor nicht beruecksichtigt und ich erhalte bei fsn2 nur Elemente vom Typ Node.

    Das dürfte eigentlich nicht so sein, da du nur statische Typen verwendest. fsn1 und fsn2 sind Zeiger auf FSNode . Wieso meinst du, dass du nur Elemente des Typs Node erhältst?



  • Nexus schrieb:

    moe szyslak schrieb:

    Wenn ich jetzt folgendes mache:

    FSNode *fsn1 = new FSNode("FSNode #1");
    	fsn1->SetLeft(new FSNode("FSNode left"));
    	fsn1->SetRight(new FSNode("FSNode right"));
    
    	FSNode *fsn2 = new FSNode(*fsn1);
    	// delete spar ich mir hier auf
    

    sind bei fsn1 left und right zwar laut dynamischer Bindung Objekte vom Typ
    FSNode, dies wird aber vom Kopierkonstructor nicht beruecksichtigt und ich erhalte bei fsn2 nur Elemente vom Typ Node.

    Das dürfte eigentlich nicht so sein, da du nur statische Typen verwendest. fsn1 und fsn2 sind Zeiger auf FSNode . Wieso meinst du, dass du nur Elemente des Typs Node erhältst?

    Naja, Node definiert und implementiert die Methode

    void PrintNode(const int space, std::ostream &out = std::cout) const;
    

    und FSNode macht das auch und ich erhalte ff. Ergebnis:

    FSNode #1 = FSNode #1
    FSNode #1 left = FSNode left
    FSNode #1 right = FSNode right
    FSNode #2 = FSNode #1
    FSNode #2 left = Node::PrintNode
    FSNode #2 right = Node::PrintNode

    wenn ich ff. ausfuehre:

    FSNode *fsn1 = new FSNode("FSNode #1");
    	fsn1->SetLeft(new FSNode("FSNode left"));
    	fsn1->SetRight(new FSNode("FSNode right"));
    
    	FSNode *fsn2 = new FSNode(*fsn1);
    
    	cout << "FSNode #1 = " << *fsn1 << endl;
    	cout << "FSNode #1 left = " << *fsn1->GetLeft() << endl;
    	cout << "FSNode #1 right = " << *fsn1->GetRight() << endl;
    
    	cout << "FSNode #2 = " << *fsn2 << endl;
    	cout << "FSNode #2 left = " << *fsn2->GetLeft() << endl;
    	cout << "FSNode #2 right = " << *fsn2->GetRight() << endl;
    
    	delete fsn2;
    	delete fsn1;
    


  • DocShoe schrieb:

    Da wirst du wohl eine virtuelle clone() Methode implementieren müssen:

    class Node
    {
    public;
       virtual ~Node();
       virtual Node* clone() const = 0;
    };
    
    class FSNode
    {
    public;
       Node* clone() const
       {
          return new FSNode( *this );
       }
    };
    

    Wenn ich das mache, dann wird Node ja eine abstrakte Klasse, die sollte aber nicht der Fall sein.
    Und wenn ich die Methoden im Node und FSNode implementiere (also ohne = 0) dann funktioniert es auch nicht!



  • Mach PrintNode() virtuell und schau nochmals.

    Nimm kein clone() , wenn es nicht absolut notwendig ist. Das ist nur ein Ansatz, um Kopiersemantik im polymorphen Kontext überhaupt zu ermöglichen. Solange du auf einer ebene direkten Zugriff auf die statischen Typen hast, implementierst du Kopien lieber über den Kopierkonstruktor.



  • moe szyslak schrieb:

    dann funktioniert es auch nicht!

    Doch, es funktioniert.

    Da du allerdings nicht sagst warum du meinst dass es nicht funktioniert (Fehlermeldung?), sag ich jetzt auch nicht warum ich glaube dass es funktioniert und wie es funktioniert.



  • OK, also doch der ganze Code:
    Node.h

    #ifndef NODE_H_
    #define NODE_H_
    
    #include <iostream>
    
    class Node {
    	friend class FSNode;
    
    private:
    	Node *left, *right;
    
    	friend std::ostream &operator<<(std::ostream &out, const Node &node);
    
    protected:
    	virtual void PrintNode(const int space, std::ostream &out = std::cout) const;
    
    public:
    	Node(Node *l = 0, Node *r = 0);
    	Node(const Node &node);
    	virtual Node* clone() const;
    	virtual ~Node();
    
    	virtual void SetLeft(Node *l);
    	virtual Node *GetLeft() const;
    	virtual void SetRight(Node *r);
    	virtual Node *GetRight() const;
    
    	virtual Node &operator=(const Node &node);
    };
    
    std::ostream &operator<<(std::ostream &out, const Node &node);
    
    #endif /* NODE_H_ */
    

    Node.cpp

    #include "Node.h"
    #include <iomanip>
    using namespace std;
    
    Node::Node(Node *l, Node *r) :
    	left(l), right(r) {
    }
    
    Node::Node(const Node &node) :
    	left(0), right(0) {
    	if (node.left != 0) {
    		this->left = new Node(*(node.left));
    	}
    	if (node.right != 0) {
    		this->right = new Node(*(node.right));
    	}
    }
    
    Node* Node::clone() const {
    	return new Node(*this);
    }
    
    Node::~Node() {
    	if (this->left != 0)
    		delete this->left;
    	if (this->right != 0)
    		delete this->right;
    }
    
    Node *Node::GetLeft() const {
    	return this->left;
    }
    
    void Node::SetRight(Node *r) {
    	this->right = r;
    }
    
    Node *Node::GetRight() const {
    	return this->right;
    }
    
    void Node::SetLeft(Node *l) {
    	this->left = l;
    }
    
    void Node::PrintNode(const int space, std::ostream &out) const {
    	out << "Node::PrintNode ";
    }
    
    Node &Node::operator=(const Node &node) {
    	if (this == &node)
    		return *this;
    
    	if (this->left != 0)
    		delete this->left;
    	if (this->right != 0)
    		delete this->right;
    
    	if (node.left != 0) {
    		this->left = new Node(*(node.left));
    	}
    	if (node.right != 0) {
    		this->right = new Node(*(node.right));
    	}
    
    	return *this;
    }
    
    std::ostream &operator<<(std::ostream &out, const Node &node) {
    	node.PrintNode(2, out);
    	return out << flush;
    }
    

    FSNode.h

    #ifndef FSNODE_H_
    #define FSNODE_H_
    
    #include "Node.h"
    
    #include <string>
    
    class FSNode: public Node {
    protected:
    	std::string name;
    	virtual void PrintNode(const int space, std::ostream &out = std::cout) const;
    
    public:
    	FSNode(std::string name);
    	FSNode(const FSNode &fsnode);
    	virtual Node* clone() const;
    	virtual ~FSNode();
    
    	void AddRight(FSNode *node);
    	std::string getName() const;
    };
    
    #endif /* FSNODE_H_ */
    

    FSNode.cpp

    FSNode::FSNode(std::string name) :
    	Node(0, 0), name(name) {
    }
    
    FSNode::FSNode(const FSNode &fsnode) :
    	Node(fsnode), name(fsnode.name) {
    }
    
    Node* FSNode::clone() const {
    	return new FSNode(*this);
    }
    
    FSNode::~FSNode() {
    }
    
    void FSNode::PrintNode(const int space, std::ostream &out) const {
    	out << this->name;
    }
    
    void FSNode::AddRight(FSNode *node) {
    	if (this->GetRight() == 0) {
    		this->SetRight(node);
    	} else {
    		Node *tmp = this->GetRight(), *prev = 0;
    		while (tmp != 0) {
    			prev = tmp;
    			tmp = tmp->GetRight();
    		}
    		prev->SetRight(node);
    	}
    }
    
    string FSNode::getName() const {
    	return this->name;
    }
    

    und eben mit dieser Implementierung und dem Test der oben beschrieben ist funktioniert es nicht.
    Hilft das weiter? Ich denke mal ich hab da was doch nicht verstanden wenn ihr sagt, dass es eigentlich gehen sollte ...



  • Ah, hab's mir doch gedacht, die Implementierung der Clone Methode ist falsch!
    Hab jetzt ein bisschen gegoogelt und hab die richtige Loesung!

    Danke fuer die Schnelle Antwort!

    P.S. natuerlich werd ich jetzt den Zuweisungsoperator auch noch anpassen muessen

    P.P.S. noch mal danke



  • Du brauchst doch eine clone() -Funktion, weil du polymorph kopierst. Ich habe zuerst gar nicht daran gedacht, dass die Node -Klasse im Kopierkonstruktor ja ihrerseits tiefe, polymorphe Kopien durchführen muss.

    Verwende nächstes Mal einen Debugger. Da kannst du sehr gut sehen, von welchem dynamischen Typ ein Objekt ist. Zur Not tuts auch RTTI direkt im Code.


Anmelden zum Antworten