Verkettete Objekte rekursiv ausgeben



  • Hallo,

    ich habe ein kleines Testprojekt erstellt, das eine Xml Datei ausliest, und diese als verkettete Struktur speichert.

    Ich weiß nicht, ob ich schon einen Fehler beim einlesen bzw. erstellen der verketteten Struktur gemacht habe, oder ob beim Ausgeben auf der Konsole etwas nicht stimmt.

    Im Wesentlichen gehts um die Datei XmlReader.cpp, in welcher die beiden Methoden "readElement" und "printNodes" zu finden sind.

    Zum Xml-lesen verwende ich TinyXML.

    Wenn sich das bitte jemand ansehen könnte, es sind nicht viele Zeilen. Ich bekomme in der Methode "printNodes" nach einer gewissen Rekursionstiefe immer die Fehlermeldung: "<error reading variable: Cannot access memory at address 0x9>"

    //EDIT:
    Hier die Source Files, da Download nicht gewünscht:

    main.cpp

    #include <iostream>
    #include "XmlReader.h"
    
    int main(int argc, char *argv[])
    {
    	if(argc < 2)
    	{
    		std::cout << "No xml file set!" << std::endl;
    		return 1;
    	}
    
    	std::string file_name = argv[1];
    
    	XmlReader reader(file_name);
    	reader.parse();
    
    	return 0;
    }
    

    XmlReader.h

    #ifndef XMLREADER_H_
    #define XMLREADER_H_
    
    #include <string>
    
    using namespace std;
    
    class TiXmlNode;
    class TiXmlElement;
    class XmlNode;
    
    class XmlReader
    {
    	public:
    		XmlReader(const string& fileName);
    		virtual ~XmlReader();
    
    		void parse();
    
    	private:
    		void readDocument(TiXmlNode* doc);
    		void readElement(TiXmlNode* node, XmlNode* data);
    		void readAttributes(TiXmlElement* element, XmlNode* data);
    
    		void printNodes(XmlNode* node);
    
    		string p_fileName;
    		XmlNode* root;
    };
    
    #endif /* XMLREADER_H_ */
    

    XmlReader.cpp

    #include "XmlReader.h"
    
    #include <iostream>
    #include <tinyxml.h>
    
    #include "XmlNode.h"
    
    using namespace std;
    
    XmlReader::XmlReader(const string& fileName) :
    	p_fileName(fileName)
    {
    	root = NULL;
    }
    
    void XmlReader::parse()
    {
    	TiXmlDocument doc(p_fileName);
    
    	if (!doc.LoadFile())
    	{
    		cout << "Xml file cannot be loaded!" << endl;
    		return;
    	}
    
    	readDocument(&doc);
    
    	printNodes(root);
    
    	delete root;
    }
    
    void XmlReader::readDocument(TiXmlNode* doc)
    {
    	if (!doc)
    		return;
    
    	TiXmlNode* child;
    
    	for (child = doc->FirstChild(); child != 0; child = child->NextSibling())
    		readElement(child, root);
    }
    
    void XmlReader::readElement(TiXmlNode* node, XmlNode* data)
    {
    	if (!node)
    		return;
    
    	XmlNode* node_child = NULL;
    
    	switch (node->Type())
    	{
    		case TiXmlNode::TINYXML_ELEMENT:
    		{
    			if (!data)
    			{
    				root = new XmlNode();
    				root->setName(node->ValueStr());
    				readAttributes(node->ToElement(), root);
    			}
    			else
    			{
    				node_child = new XmlNode();
    				data->addChild(node_child);
    
    				node_child->setName(node->ValueStr());
    				readAttributes(node->ToElement(), node_child);
    			}
    
    			TiXmlNode* child;
    			for (child = node->FirstChild(); child != 0; child = child->NextSibling())
    			{
    				if (node_child)
    					readElement(child, node_child);
    				else
    					readElement(child, root);
    			}
    
    			break;
    		}
    		case TiXmlNode::TINYXML_TEXT:
    		{
    			TiXmlText *text = node->ToText();
    			data->setValue(text->ValueStr());
    			break;
    		}
    	}
    }
    
    void XmlReader::readAttributes(TiXmlElement* element, XmlNode* data)
    {
    	if (!element)
    		return;
    
    	TiXmlAttribute* attribute = element->FirstAttribute();
    	while (attribute)
    	{
    		data->addAttribute(attribute->NameTStr(), attribute->ValueStr());
    		attribute = attribute->Next();
    	}
    }
    
    void XmlReader::printNodes(XmlNode* node)
    {
    	if (!node)
    		return;
    
    	cout << node->getName() << " --> " << node->getValue() << endl;
    
    	if (node->getChildren().size() > 0)
    	{
    		list<XmlNode*>::iterator it = node->getChildren().begin();
    
    		while (it != node->getChildren().end())
    		{
    			printNodes((*it));
    			it++;
    		}
    	}
    	else
    		return;
    }
    
    XmlReader::~XmlReader()
    {
    	delete root;
    }
    

    XmlNode.h

    #ifndef XMLNODE_H_
    #define XMLNODE_H_
    
    #include <string>
    #include <map>
    #include <list>
    
    using namespace std;
    
    class XmlNode
    {
    	public:
    		XmlNode();
    		virtual ~XmlNode();
    
    		void setName(const string &name);
    		string getName() const { return pName; }
    
    		void setValue(const string &value);
    		string getValue() const { return pValue; }
    
    		void addChild(XmlNode* child);
    		void setChildren(const list<XmlNode*>& children);
    		list<XmlNode*> getChildren() const { return pChildren; }
    
    		void addAttribute(const string &key, const string &value);
    		void setAttributes(const map<string, string>& attributes);
    		map<string, string> getAttributes() const { return pAttributes; }
    
    	private:
    		string pName;
    		string pValue;
    		list<XmlNode*> pChildren;
    		map<string, string> pAttributes;
    };
    
    #endif /* XMLNODE_H_ */
    

    XmlNode.cpp

    #include "XmlNode.h"
    
    XmlNode::XmlNode()
    {
    
    }
    
    XmlNode::~XmlNode()
    {
    	pChildren.clear();
    	pAttributes.clear();
    }
    
    void XmlNode::setChildren(const list<XmlNode*> & children)
    {
    	pChildren = children;
    }
    
    void XmlNode::addAttribute(const string & key, const string & value)
    {
    	if(pAttributes.count(key) == 0)
    		pAttributes[key] = value;
    }
    
    void XmlNode::setValue(const string & value)
    {
    	pValue = value;
    }
    
    void XmlNode::setAttributes(const map<string,string> & attributes)
    {
    	pAttributes = attributes;
    }
    
    void XmlNode::addChild(XmlNode *child)
    {
    	pChildren.push_back(child);
    }
    
    void XmlNode::setName(const string & name)
    {
    	pName = name;
    }
    


  • HerbertB schrieb:

    Das programm befindet sich im Anhang.

    Nein...

    edit: sachen runterladen kommt hier im forum ganz schlecht an. poste den entsprechenden source code hier...



  • Hallo Herbert,

    in der Datei XmlNode.h Zeile 24 gibst Du std::list<XmlNode*> per Value zurück. D.h. in der Funktion XmlReader::printNodes Datei XmlReader.cpp Zeile 112 wird eine Kopie der Liste auf den Stack gelegt, dort der Iterator auf begin abgeholt und mit Verlassen der Zeile 112 ist die Liste gelöscht und 'it' ist ein Iterator auf einen Container, der nicht mehr da ist. Alles weitere mündet in undefiniertem Verhalten.
    Ein schneller Fix in XmlReader.cpp wäre

    void XmlReader::printNodes(XmlNode* node)
    {
        if (!node)
            return;
    
        cout << node->getName() << " --> " << node->getValue() << endl;
    
        list<XmlNode*> childs = node->getChildren(); // Kopie anlegen und nur auf der Kopie arbeiten
        for( list<XmlNode*>::iterator iNode = childs.begin(); iNode != childs.end(); ++iNode )
            printNodes(*iNode);
    }
    

    Weiter gehst Du sehr großzügig mit dem Member XmlReader::root um. Füge mindestens in der Methode XmlReader::parse hinter dem delete root in Zeile 30 ein root=0; ein, sonst stürzt das Programm im Destruktor in Zeile 126 ab. Aber ich würde Dir wärmstens einen Smartpointer (z.B.: std::uniqe_ptr) empfehlen.

    Gruß
    Werner



  • Vielen dank für die hilfe, ich werde das mit den smart-pointer auch umsetzen.


Anmelden zum Antworten