您的位置:首页 > 编程语言 > C语言/C++

tinyXml和stl中map使用的一点坑

2017-07-28 19:50 369 查看
本文中参考的资料:
http://blog.csdn.net/scythe666/article/details/51718864 http://www.cnblogs.com/lizhanwu/p/4428990.html
1、场景回现

前段时间我在参考了上面两个资料之后简单的用C++实现了一下反射机制,实现后想通过xml文件读入所想要的数据来创建类,但是通过tinyXml读取数据后,在map想通过这个数据来找到我所需要的类,发现我传入的这个参数name确实在我map里有存在,但是经过stl中map自带的find函数后,发现找到的结果是这个map的结尾,也就是说通过这个name我并不能在map中找到我所需要的数据。

以下是出现以上错误的全部代码:

#include<map>
#include<iostream>

#include"xml\tinyxml.h"
using namespace std;

//一个宏定义

#define REGISTER(className) \
RegisterAction::registerClass(#className,className::create)

class Base {
public:
Base() {}
~Base() {}

// 重写实现
virtual void m_print() = 0;

};

typedef Base* (*ClassCreateCB)();

class TestFactory {
public:
Base* GetClassByName(const char* name) {
map<const char*, ClassCreateCB>::iterator iter;
iter = n_fMap.find(name);
if (iter == n_fMap.end()) {
cout << name << ":没有这个类的构造函数" << endl;
return NULL;
}
else {
return iter->second();
}
}
//换言之是注册
void addClass(const char* name, ClassCreateCB fun) {
n_fMap.insert(std::make_pair(name, fun));
}
static TestFactory* getInstance() {
if(instance==NULL){
instance = new TestFactory();
}
return instance;
}
private:
map<const char*, ClassCreateCB>n_fMap;
static TestFactory* instance;
//工厂类也是一种单例类
TestFactory() {};
};

TestFactory* TestFactory::instance = NULL;

class TestA :public Base {
public:
TestA() {};
~TestA() {};

static Base* create() {
return new TestA;
}

virtual void m_print() override{
cout << "this is TestA" << endl;
}
};

class TestB :public Base {
public:
TestB() {};
~TestB() {};

static Base* create() {
return new TestB;
}

virtual void m_print() override {
cout << "this is TestB" << endl;
}
};
class TestC :public Base {
public:
TestC() {};
~TestC() {};

static Base* create() {
return new TestC;
}

virtual void m_print() override {
cout << "this is TestC" << endl;
}
};
class TestD :public Base {
public:
TestD() {};
~TestD() {};

static Base* create() {
return new TestD;
}

virtual void m_print() override {
cout << "this is TestD" << endl;
}
};

//工具类
class RegisterAction {
public:
static RegisterAction* getInstance() {
if (instance == NULL) {
instance = new RegisterAction;
}
return instance;
}
static void registerClass(const char* name, ClassCreateCB fun) {
TestFactory::getInstance()->addClass(name, fun);
}

void registerAll() {
REGISTER(TestA);
REGISTER(TestB);
REGISTER(TestC);
REGISTER(TestD);
}

private:
static RegisterAction* instance;
RegisterAction() {};
};

RegisterAction* RegisterAction::instance = NULL;

int main() {
RegisterAction::getInstance()->registerAll();

const char * path = "class.xml";
TiXmlDocument doc(path);

bool flag = doc.LoadFile();
if (!flag) {
cout << "check for the error" << endl;
}

TiXmlNode* root = doc.FirstChild("plist");
const char* c_strA = "TestA";
const char* c_strB = "TestB";
const char* c_strC = "TestC";
const char* c_strD = "TestD";

if (!root) {
cout << "plist not exist" << endl;
}
else {
TiXmlElement* ele;
for (TiXmlNode* node = root->FirstChild(); node; node = node->NextSibling()) {
ele = node->ToElement();

const char* str = (ele->Attribute("className"));
Base* base = TestFactory::getInstance()->GetClassByName(str);
if (base == NULL) {
cout << "不存在" << endl;
}
else base->m_print();
}
}
system("pause");
}

2、问题解决:
当时出现这个问题的时候我认为是编码上的问题,各种修改后无果便放到一旁了,今天再一次用到tinyXml的时候发现了一个类似的错误,观察了内存之后,发现tinyXml是先将这个文件全部读入到内存中,然后通过tinyXml来获取所需的数据时,返回出来的数据就是在这个内存块上面的,然后当TiXmlDocument的析构函数被调用时,这个内存区上所存放的所有数据都会被清空!

到这里问题就很明确了,比如说一个在Xml中存放的一个字符串“TestA”,这个字符串所在的内存地址和通过一个const char*所创建出来的字符串“TestA”是不一样的,后者是存储在专门存放文字常量的文字常量区中,而从这里我们可以知道,之前的错误发生有一个原因就是因为stl中的find之间的比较应该是直接通过==操作符来实现,对于一个const char*类型的数据,他们==操作符所比较的是两个数据之间的地址,只有当地址一样时这两个值才会一样,也就因此造成了find函数的失效。

解决方法其实很简单,将map中的const char*换成string就解决了,如下:

map<string, ClassCreateCB>n_fMap;
对于string而言其==所比较的是其内容,会造成这样的原因也就消失了,而且string在创建的时候等于会复制一段这个字符串的内容,也就避免了你创建了一个指向tinyXml存放的字符串后由于内存被收回导致的一些错误。

第一次写文,见谅
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C++ xml map