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 FunktionXmlReader::printNodesDatei 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ärevoid 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::rootum. Füge mindestens in der MethodeXmlReader::parsehinter demdelete rootin Zeile 30 einroot=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.