开发者

C++ Factory pattern using templates for self registering

My questions correspond to the answer from Johannes in Is there a way to instantiate objects from a string holding their class name? and the recent comment from Spencer Rose. Since I cannot add a comment there, I decided to start a new question.

Johannes suggestion is what I need. I implemented it in exact the same way but I have an unresolved external symbol linker error using VS2008 which seems to have something to do with the map. I am trying since days to solve it. Today I read the comment from Spencer and added the line he suggests

BaseFactory::map_type BaseFactory::map = new map_type();

to the Base.hpp. Now I get a LNK2005 error

Derivedb.obj : error LNK2005:
"private: static class std::map<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class Base * (__cdecl*)(void),struct std::less<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >,class std::alloc开发者_运维知识库ator<struct std::pair<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const ,class Base * (__cdecl*)(void)> > > * BaseFactory::map"
(?map@BaseFactory@@0PAV?$map@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@P6APAVBase@@XZU?$less@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@2@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@P6APAVBaser@@XZ@std@@@2@@std@@A)
already defined in Switcher.obj

Project.exe : fatal error LNK1169: one or more multiply defined symbols found) 

instead of the LNK2001 error

(Switcher.obj : error LNK2001: unresolved external symbol "private: static class std::map<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class Base * (__cdecl*)(void),struct std::less<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >,class std::allocator<struct std::pair<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const ,class Base * (__cdecl*)(void)> > > * BaseFactory::map" (?map@BaseFactory@@0PAV?$map@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@P6APAVBase@@XZU?$less@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@2@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@P6APAVBase@@XZ@std@@@2@@std@@A)
1>Derivedb.obj : error LNK2001: unresolved external symbol "private: static class std::map<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class Base * (__cdecl*)(void),struct std::less<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >,class std::allocator<struct std::pair<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const ,class Base * (__cdecl*)(void)> > > * BaseFactory::map" (?map@BaseFactory@@0PAV?$map@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@P6APAVBase@@XZU?$less@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@2@V?$allocator@U?$pair@$$CBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@P6APAVBase@@XZ@std@@@2@@std@@A)
1>Project.exe : fatal error LNK1120: 1 unresolved externals)

which means that I may have defined it twice?? Please could Spencer or somebody post the improved base.hpp code. It is such an important solution that it surely will be helpful for many more novice C++ programmers.

Second question: --> This problem is solved! Thanks!

I need some function declarations in base.hpp. Those should have been abstract in the base class and implemented in the subclasses (e.g. Derivedb.cpp ). But

public:
       virtual ReadInFile(std::string path, std::string filename) = 0;

in base.hpp gave a compiler error. Removing "= 0" resolved the compiler error. But now I have another unresolved external symbol LNK2001 error

Derivedb.obj: error LNK2001: unresolved external symbol
"public: virtual __thiscall Base::ReadInFile(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)"
(?ReadInFile@Base@@UAE_NPAV@@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@1@Z).

I call it in another cpp file

Base* importer =  BaseFactory::createInstance("DerivedB");
importer->ReadInFile(m_path, m_filename);

Maybe it is not clear which function (base or subclass) need to be called since it is not abstract in the base class??? Is there any way of solving this problem? Thank you!


From the LNK2005 error, your BaseFactory class has a static field called map, right?

Static fields in C++ have to be "declared" inside the class's header file, and "implemented" in the class's source file.

Here is a simplified example of how to set this up. In this case, in the BaseFactory.h file, you should have the static field declared:

class BaseFactory
{
private:
    static int map;
};

And in the BaseFactory.cpp file, the static field gets implemented:

int BaseFactory::map = 392;

The error message from the linker is saying that the BaseFactory::map static field got implemented in both the Derivedb.cpp file and the Switcher.cpp files.

Even if the implementation (... BaseFactory::map = ...) isn't in either of those files, you'll get the same error if you put the implementation in the BaseFactory.h header file. The C++ preprocessor just blindly includes code headers, and the linker can't tell whether the implementation is in the Switcher.cpp file or some file that Switcher.cpp included.

Just like methods need to be declared in .h files and implemented in .cpp files, the same goes for static fields.


map is the name of a template class in the STL, and therefore a poor choice for a variable name. This may or may not be related to the duplicate definition error you are getting.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜