本文中参考的资料:
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");
}
当时出现这个问题的时候我认为是编码上的问题,各种修改后无果便放到一旁了,今天再一次用到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存放的字符串后由于内存被收回导致的一些错误。
第一次写文,见谅