tinyxml源码解析(中)
前言:
之前趁着这段时间比较空闲,也因为听闻tinyxml大名,因此就阅读了tinyxml的源码.在上一篇博文中<tinyxml源码解析(上)>中对tinyxml整体架构以及其中两个基类TiXmlBase,TiXmlNode进行了注释解析.不过文章写得有点啰嗦,太过详细.因此这篇文章将提取其中的部分设计而不是对于每个函数进行注释.
本文将主要介绍tinyxml对于attribute的处理,各个元素类的简单介绍以及tinyxml的整体处理流程.
正文:
还是先回顾下之前看的tinyxml的结构图吧.
从图上我们可以看到,TiXmlAttribute并不是继承与TiXmlNode中,虽然其是xml的元素之一,在实现上很多也是element的叶节点,不过tinyxml并没有这么做.可以先看下TiXmlAttribute的声明.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class TiXmlAttribute : public TiXmlBase 2 { 3 friend class TiXmlAttributeSet;//TiXMlAttrubteSet之后我们将会看到 4 public: 5 //....构造函数 6 7 //返回属性的值 8 const char* Name() const { return name.c_str(); } 9 const char* Value() const { return value.c_str(); } 10 //.... 11 //提供了比IntValue()更好的错误检查.后头还是调用了sscanf进行处理,而IntValue则是采用了atoi 12 //QueryXXXValue系列都是采用了sscanf进行处理. 13 int QueryIntValue( int* _value ) const; 14 int QueryDoubleValue( double* _value ) const; 15 16 //设置值,对于有类型转化的,采用了sprintf进行处理 17 void SetName( const char* _name ) { name = _name; } 18 void SetValue( const char* _value ) { value = _value; } 19 20 void SetIntValue( int _value ); 21 void SetDoubleValue( double _value ); 22 23 //提供了string的版本 24 ///... 25 26 ///简单的比较函数 27 ///.... 28 ///Parse将在下面统一讲解 29 virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); 30 31 32 private: 33 TiXmlAttribute( const TiXmlAttribute& ); // not implemented. 34 void operator=( const TiXmlAttribute& base ); // not allowed. 35 36 TiXmlDocument* document; // A pointer back to a document, for error reporting. 37 TIXML_STRING name; 38 TIXML_STRING value; 39 TiXmlAttribute* prev; 40 TiXmlAttribute* next; 41 };
TiXmlAttribute提供了少部分功能的转化函数.不过特殊的是其Next函数以及Previous函数的实现比较特别.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 const TiXmlAttribute* TiXmlAttribute::Next() const 2 { 3 //这里判断是否到尾部的方式是判断next的成员的值 4 if ( next->value.empty() && next->name.empty() ) 5 return 0; 6 return next; 7 } 8 9 const TiXmlAttribute* TiXmlAttribute::Previous() const 10 { 11 // 这里也是 12 if ( prev->value.empty() && prev->name.empty() ) 13 return 0; 14 return prev; 15 }
原因在于,tinyxml使用了循环双向链表进行处理.看下面的TiXmlAttributeSet就可以看得出了.作者给出的理由是:他喜欢循环链表.另外,说明了相对于典型双向链表的独立性(这个我也没搞明白是怎么回事).
TiXmlAttributeSet是一个工具类,其负责管理Element中的attribute.提供了添加,删除,查找等功能,使得代码更为简洁.
1 //折叠起来总是有BUG.打不开.所以就只要不折叠了... 2 class TiXmlAttributeSet 3 { 4 public: 5 //构造和析构函数,构造函数就是把sentinel的头尾连在一起,而析构则是两句assert,判断是否所有元素被移除. 6 7 void Add( TiXmlAttribute* attribute ); 8 void Remove( TiXmlAttribute* attribute ); 9 10 //有元素返回,没元素返回null 11 const TiXmlAttribute* First() const { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } 12 TiXmlAttribute* First() { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } 13 const TiXmlAttribute* Last() const { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } 14 TiXmlAttribute* Last() { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } 15 16 //查找 17 TiXmlAttribute* Find( const char* _name ) const; 18 TiXmlAttribute* FindOrCreate( const char* _name ); 19 ///stl版本 20 ///... 21 22 private: 23 //因为TiXmlAttribute禁止复制,因此AttributeSet也禁止 24 TiXmlAttributeSet( const TiXmlAttributeSet& ); 25 void operator=( const TiXmlAttributeSet& ); 26 27 TiXmlAttribute sentinel;//循环链表的头.其目的在于做为头和尾的区分. 28 };
可以看看循环链表的处理.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 void TiXmlAttributeSet::Add( TiXmlAttribute* addMe ) 2 { 3 #ifdef TIXML_USE_STL 4 assert( !Find( TIXML_STRING( addMe->Name() ) ) ); // Shouldn't be multiply adding to the set. 5 #else 6 assert( !Find( addMe->Name() ) ); // Shouldn't be multiply adding to the set. 7 #endif 8 9 addMe->next = &sentinel; 10 addMe->prev = sentinel.prev; 11 12 sentinel.prev->next = addMe; 13 sentinel.prev = addMe; 14 } 15 16 void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe ) 17 { 18 TiXmlAttribute* node; 19 20 for( node = sentinel.next; node != &sentinel; node = node->next ) 21 { 22 if ( node == removeMe ) 23 { 24 node->prev->next = node->next; 25 node->next->prev = node->prev; 26 node->next = 0; 27 node->prev = 0; 28 return; 29 } 30 } 31 assert( 0 ); // we tried to remove a non-linked attribute.
在查看了attribute的处理后,我们就可以看看element的处理了.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class TiXmlElement : public TiXmlNode 2 { 3 public: 4 5 //构造,复制,析构函数 6 ///... 7 8 ///一系列的Attribute函数仅仅就是调用attributeset中的find,查找到后调用attribute的query函数 9 ///其他都类似 10 const char* Attribute( const char* name ) const 11 { 12 const TiXmlAttribute* node = attributeSet.Find( name ); 13 if ( node ) 14 return node->Value(); 15 return 0; 16 } 17 18 //set函数也不例外,就是find然后set 19 void TiXmlElement::SetAttribute( const char * name, int val ) 20 { 21 TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); 22 if ( attrib ) { 23 attrib->SetIntValue( val ); 24 } 25 } 26 27 //可想而知,remove肯定也是一样的,调用attributeset的函数进行处理 28 void RemoveAttribute( const char * name ); 29 30 //获取其中第一个文字节点 31 const char* GetText() const; 32 33 /// 复制当前节点,重写了TiXmlNode的纯虚函数 34 virtual TiXmlNode* Clone() const; 35 //打印函数, 36 virtual void Print( FILE* cfile, int depth ) const; 37 38 //parse函数之后统一说 39 virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); 40 41 //这部分设计的很精巧.在父类TiXmlNode中,所有的ToXXXX函数返回皆为null.而在子类中相应的重写函数,使之转化成为可能.而转化成其他类的函数仍然不可以. 42 virtual const TiXmlElement* ToElement() const { return this; } 43 virtual TiXmlElement* ToElement() { return this; } 44 45 virtual bool Accept( TiXmlVisitor* visitor ) const; 46 47 protected: 48 //复制函数,提供给复制构造函数 49 void CopyTo( TiXmlElement* target ) const; 50 void ClearThis(); 51 //获取element中的值.在parse中被调用. 52 const char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding ); 53 54 private: 55 TiXmlAttributeSet attributeSet;//管理attribute列表 56 };
剩下的几个元素,如declaration,unknown,text,comment都和element差别不大.我们直接来看TiXmlDocument:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class TiXmlDocument : public TiXmlNode 2 { 3 public: 4 //构造函数 5 TiXmlDocument() 6 { 7 tabsize = 4;//设定输出tab格式 8 useMicrosoftBOM = false;//设定是否采用ms的utf8-bom格式 9 ClearError(); 10 } 11 12 //复制构造函数 13 TiXmlDocument( const TiXmlDocument& copy ); 14 TiXmlDocument& operator=( const TiXmlDocument& copy ) 15 { 16 Clear(); 17 copy.CopyTo( this );//采用了private的copyTo函数 18 return *this; 19 } 20 21 virtual ~TiXmlDocument() {}//析构没什么好做的 22 23 //保存和读取文本函数, 24 bool LoadFile( FILE*, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); 25 bool SaveFile( FILE* ) const; 26 27 //分析,下面将主要分析这个函数 28 virtual const char* Parse( const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); 29 30 //带格式输出 31 virtual void Print( FILE* cfile, int depth = 0 ) const; 32 void SetError( int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding ); 33 34 //同TiXmlElement 35 virtual const TiXmlDocument* ToDocument() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. 36 virtual TiXmlDocument* ToDocument() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. 37 38 /** Walk the XML tree visiting this node and all of its children. 39 */ 40 virtual bool Accept( TiXmlVisitor* content ) const; 41 42 protected : 43 // 重写的Clone函数 44 virtual TiXmlNode* Clone() const; 45 #ifdef TIXML_USE_STL 46 virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); 47 #endif 48 49 private: 50 //复制的内部函数 51 void CopyTo( TiXmlDocument* target ) const; 52 53 bool error; 54 int errorId; 55 TIXML_STRING errorDesc; 56 int tabsize; 57 TiXmlCursor errorLocation;//定位错误,包涵了行数和列数 58 bool useMicrosoftBOM; // 使用utf-bom 59 };
大部分只是简单的设置,我们就着重挑几个感兴趣的看看.
首先是文件操作,LoadFile以及SaveFile
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 //载入数据 2 bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding ) 3 { 4 if ( !file ) 5 { 6 SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); 7 return false; 8 } 9 10 // 初始化数据 11 Clear(); 12 location.Clear(); 13 14 // 采用fseek的方式来获取文件的大小。并且预分配数据大小,加速大文件处理 15 long length = 0; 16 fseek( file, 0, SEEK_END ); 17 length = ftell( file ); 18 fseek( file, 0, SEEK_SET ); 19 20 // 比较奇怪的情况。 21 if ( length <= 0 ) 22 { 23 SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); 24 return false; 25 } 26 27 //先new需要读取的数据的大小. 28 char* buf = new char[ length+1 ]; 29 buf[0] = 0; 30 31 //然后一次性fread进来. 32 if ( fread( buf, length, 1, file ) != 1 ) { 33 delete [] buf; 34 SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); 35 return false; 36 } 37 38 // 统一回车符,采用p,q两个指针进行处理. 39 //将\r\n,\n,\r统一转化为\n. 40 const char* p = buf; // the read head 41 char* q = buf; // the write head 42 const char CR = 0x0d; 43 const char LF = 0x0a; 44 45 buf[length] = 0; 46 while( *p ) { 47 assert( p < (buf+length) ); 48 assert( q <= (buf+length) ); 49 assert( q <= p ); 50 51 //是\r 52 if ( *p == CR ) { 53 *q++ = LF;//转化为\n,并且判断下一个字符,,如果是\n,则跳过 54 p++; 55 if ( *p == LF ) { 56 p++; 57 } 58 } 59 else { 60 *q++ = *p++; 61 } 62 } 63 assert( q <= (buf+length) ); 64 *q = 0; 65 66 Parse( buf, 0, encoding );//进行解析,生成xml节点树 67 68 delete [] buf;//删除缓存的数据 69 return !Error(); 70 } 71 //save相对简单的多 72 bool TiXmlDocument::SaveFile( FILE* fp ) const 73 { 74 //如果是ms的格式,则写一个UTF8的头 75 if ( useMicrosoftBOM ) 76 { 77 const unsigned char TIXML_UTF_LEAD_0 = 0xefU; 78 const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; 79 const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; 80 81 fputc( TIXML_UTF_LEAD_0, fp ); 82 fputc( TIXML_UTF_LEAD_1, fp ); 83 fputc( TIXML_UTF_LEAD_2, fp ); 84 } 85 //然后调用Print进行格式化输出 86 Print( fp, 0 ); 87 return (ferror(fp) == 0); 88 }
接下来是我们用于输出的Print函数:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 void TiXmlDocument::Print( FILE* cfile, int depth ) const 2 { 3 assert( cfile ); 4 for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) 5 { 6 //使用虚函数继承的方式,调用相应的处理函数 7 node->Print( cfile, depth ); 8 fprintf( cfile, "\n" ); 9 } 10 }
这里用到了多态的方式,来实现相应的函数调用.
而生成所有xml树的方法Parse也是想同的想法.
这里就只列举了TiXmlDocument和TiXmlElement的方法.其他的都是类似的处理方式.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 //在TiXmlDocument::Parse中调用,用于判断接下来数据p中是什么类型的节点,并且生成该类型的节点返回 2 TiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding ) 3 { 4 TiXmlNode* returnNode = 0; 5 6 p = SkipWhiteSpace( p, encoding ); 7 //跳过空格后应该是'<',否则要么出错要么结束,返回null 8 if( !p || !*p || *p != '<' ) 9 { 10 return 0; 11 } 12 13 p = SkipWhiteSpace( p, encoding ); 14 15 if ( !p || !*p ) 16 { 17 return 0; 18 } 19 20 21 22 const char* xmlHeader = { "<?xml" }; 23 const char* commentHeader = { "<!--" }; 24 const char* dtdHeader = { "<!" }; 25 const char* cdataHeader = { "<![CDATA[" }; 26 27 //分别判断接下来是什么 28 //<?xml 代表是xml文件的declaration 29 if ( StringEqual( p, xmlHeader, true, encoding ) ) 30 { 31 #ifdef DEBUG_PARSER 32 TIXML_LOG( "XML parsing Declaration\n" ); 33 #endif 34 returnNode = new TiXmlDeclaration(); 35 } 36 else if ( StringEqual( p, commentHeader, false, encoding ) ) 37 {//<!--代表是注释 38 #ifdef DEBUG_PARSER 39 TIXML_LOG( "XML parsing Comment\n" ); 40 #endif 41 returnNode = new TiXmlComment(); 42 } 43 else if ( StringEqual( p, cdataHeader, false, encoding ) ) 44 {//<![CDATA代表CDATA的数据,不过xml并没有相应去解析,仅仅做为文字处理 45 #ifdef DEBUG_PARSER 46 TIXML_LOG( "XML parsing CDATA\n" ); 47 #endif 48 TiXmlText* text = new TiXmlText( "" ); 49 text->SetCDATA( true ); 50 returnNode = text; 51 } 52 else if ( StringEqual( p, dtdHeader, false, encoding ) ) 53 {//<!代表是dtd,不过由于不支持,所以返回为unknown 54 #ifdef DEBUG_PARSER 55 TIXML_LOG( "XML parsing Unknown(1)\n" ); 56 #endif 57 returnNode = new TiXmlUnknown(); 58 } 59 else if ( IsAlpha( *(p+1), encoding ) 60 || *(p+1) == '_' ) 61 {//如果是字符,则说明为element 62 #ifdef DEBUG_PARSER 63 TIXML_LOG( "XML parsing Element\n" ); 64 #endif 65 returnNode = new TiXmlElement( "" ); 66 } 67 else 68 {//否则就是unknown 69 #ifdef DEBUG_PARSER 70 TIXML_LOG( "XML parsing Unknown(2)\n" ); 71 #endif 72 returnNode = new TiXmlUnknown(); 73 } 74 75 if ( returnNode ) 76 { 77 // Set the parent, so it can report errors 78 returnNode->parent = this; 79 } 80 return returnNode; 81 } 82 83 //TiXmlDocument的Parse,在LoadFile时被调用 84 const char* TiXmlDocument::Parse( const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding ) 85 { 86 ClearError(); 87 88 if ( !p || !*p ) 89 { 90 SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); 91 return 0; 92 } 93 94 location.Clear(); 95 if ( prevData ) 96 { 97 location.row = prevData->cursor.row; 98 location.col = prevData->cursor.col; 99 } 100 else 101 { 102 location.row = 0; 103 location.col = 0; 104 } 105 //构造parsing data,用于记录数据的位置 106 TiXmlParsingData data( p, TabSize(), location.row, location.col ); 107 location = data.Cursor(); 108 109 if ( encoding == TIXML_ENCODING_UNKNOWN ) 110 { 111 // 判断是否是utf8 bom格式 112 const unsigned char* pU = (const unsigned char*)p; 113 if ( *(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0 114 && *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1 115 && *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2 ) 116 { 117 encoding = TIXML_ENCODING_UTF8; 118 useMicrosoftBOM = true; 119 } 120 } 121 122 //跳过所有的空格符,包括\n\r\t ' '等.直接使用english的方式.在utf8情况下也很正常. 123 p = SkipWhiteSpace( p, encoding ); 124 if ( !p ) 125 { 126 SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); 127 return 0; 128 } 129 130 while ( p && *p ) 131 { 132 //利用Identify的方法来判断是某种元素,并且使用该种元素进行解析接下来的数据. 133 TiXmlNode* node = Identify( p, encoding ); 134 if ( node ) 135 { 136 p = node->Parse( p, &data, encoding ); 137 //将节点链接到最后的子节点之后. 138 //LinkEndChild将节点的所有权直接改到父节点.减少一次复制 139 LinkEndChild( node ); 140 } 141 else 142 { 143 break; 144 } 145 146 // Did we get encoding info? 147 if ( encoding == TIXML_ENCODING_UNKNOWN 148 && node->ToDeclaration() ) 149 { 150 TiXmlDeclaration* dec = node->ToDeclaration(); 151 const char* enc = dec->Encoding(); 152 assert( enc ); 153 154 if ( *enc == 0 ) 155 encoding = TIXML_ENCODING_UTF8; 156 else if ( StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) ) 157 encoding = TIXML_ENCODING_UTF8; 158 else if ( StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) ) 159 encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice 160 else 161 encoding = TIXML_ENCODING_LEGACY; 162 } 163 164 p = SkipWhiteSpace( p, encoding ); 165 } 166 167 // Was this empty? 168 if ( !firstChild ) { 169 SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding ); 170 return 0; 171 } 172 173 return p; 174 } 175 176 //TiXmlElement::Parse,在TiXmlDocument中,检测到是element节点后被调用 177 //p代表读取的文本数据 178 const char* TiXmlElement::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) 179 { 180 p = SkipWhiteSpace( p, encoding ); 181 TiXmlDocument* document = GetDocument(); 182 183 //读取element时不应该出现结束的情况,返回错误 184 if ( !p || !*p ) 185 { 186 if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, 0, 0, encoding ); 187 return 0; 188 } 189 190 if ( data ) 191 { 192 //计算目前处理的字符所在的位置 193 //会有一定的性能影响.可以选择关闭 194 data->Stamp( p, encoding ); 195 location = data->Cursor(); 196 } 197 198 //这个应该不会遇到 199 if ( *p != '<' ) 200 { 201 if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, p, data, encoding ); 202 return 0; 203 } 204 205 p = SkipWhiteSpace( p+1, encoding ); 206 207 //获取节点的名字 208 const char* pErr = p; 209 210 //TiXMlElement的工具函数,读取tag的name 211 p = ReadName( p, &value, encoding ); 212 if ( !p || !*p ) 213 { 214 if ( document ) document->SetError( TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, pErr, data, encoding ); 215 return 0; 216 } 217 218 //构造结尾标识符 219 TIXML_STRING endTag ("</"); 220 endTag += value; 221 222 // 获取element中的属性,结尾标识符 223 while ( p && *p ) 224 { 225 pErr = p; 226 p = SkipWhiteSpace( p, encoding ); 227 if ( !p || !*p ) 228 { 229 if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding ); 230 return 0; 231 } 232 if ( *p == '/' ) 233 { 234 ++p; 235 // 是空的tag,不具有子节点, <xxx /> 236 if ( *p != '>' ) 237 { 238 if ( document ) document->SetError( TIXML_ERROR_PARSING_EMPTY, p, data, encoding ); 239 return 0; 240 } 241 return (p+1); 242 } 243 else if ( *p == '>' ) 244 { 245 // 处理完属性,下一步处理子节点 246 ++p; 247 //ReadValue会处理<x> </x>之间内的所有数据,包括文字,子节点.处理结束后返回 248 p = ReadValue( p, data, encoding ); 249 if ( !p || !*p ) { 250 //到结尾了,这不应该发生 251 if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding ); 252 return 0; 253 } 254 255 if ( StringEqual( p, endTag.c_str(), false, encoding ) ) 256 { 257 p += endTag.length(); 258 p = SkipWhiteSpace( p, encoding ); 259 if ( p && *p && *p == '>' ) { 260 ++p; 261 return p; 262 } 263 if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding ); 264 return 0; 265 } 266 else 267 { 268 if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding ); 269 return 0; 270 } 271 } 272 else 273 { 274 // 是属性,进行处理 275 //生成一个属性节点,然后调用该节点parse接下来的数据 276 TiXmlAttribute* attrib = new TiXmlAttribute(); 277 if ( !attrib ) 278 { 279 return 0; 280 } 281 282 attrib->SetDocument( document ); 283 pErr = p; 284 p = attrib->Parse( p, data, encoding ); 285 286 if ( !p || !*p ) 287 { 288 if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding ); 289 delete attrib; 290 return 0; 291 } 292 293 // 重复属性,直接报错,返回 294 #ifdef TIXML_USE_STL 295 TiXmlAttribute* node = attributeSet.Find( attrib->NameTStr() ); 296 #else 297 TiXmlAttribute* node = attributeSet.Find( attrib->Name() ); 298 #endif 299 if ( node ) 300 { 301 if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding ); 302 delete attrib; 303 return 0; 304 } 305 306 //将该节点加入到attributeset中. 307 attributeSet.Add( attrib ); 308 } 309 } 310 return p; 311 }
后语:
关于tinyxml的介绍大致到这里了.本文介绍了tinyxml的整体处理流程以及其实现的一些细节.之前也有读者提示说tinyxml很慢.而慢的原因有很多,一个是其用了很多多态,而多态是有一定的代价的.另外,其整体实现都是以树的方式完成的,包括element的属性是以链表的方式实现的,查找等效率都比较低下.