C++ 工厂模式使用模板进行自注册

标签 c++ templates linker factory

我的问题与 Johannes 在 Is there a way to instantiate objects from a string holding their class name? 中的回答相对应以及 Spencer Rose 最近的评论。由于我不能在那里添加评论,所以我决定开始一个新问题。

Johannes 的建议正是我所需要的。我以完全相同的方式实现了它,但是我使用 VS2008 遇到了一个 Unresolved external symbol 链接器错误,这似乎与 map 有关。几天以来我一直在努力解决它。今天我阅读了 Spencer 的评论并添加了他建议的行

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

到 Base.hpp。现在我得到一个 LNK2005 错误

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::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@@P6APAVBaser@@XZ@std@@@2@@std@@A)
already defined in Switcher.obj

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

而不是 LNK2001 错误

(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)

这意味着我可能定义了两次?? 请 Spencer 或其他人发布改进的 base.hpp 代码。这是一个如此重要的解决方案,它肯定会对更多的 C++ 新手程序员有所帮助。

第二个问题: --> 这个问题解决了!谢谢!

我需要在 base.hpp 中声明一些函数。这些应该在基类中是抽象的并在子类中实现(例如 Derivedb.cpp )。但是

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

在 base.hpp 中给出了一个编译器错误。删除“= 0”解决了编译器错误。但是现在我有另一个 Unresolved external symbol LNK2001 错误

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).

我在另一个cpp文件中调用它

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

可能不清楚需要调用哪个函数(基类或子类),因为它在基类中不是抽象的???有什么办法可以解决这个问题吗?谢谢你!

最佳答案

从 LNK2005 错误来看,您的 BaseFactory 类有一个名为 map 的静态字段,对吧?

C++ 中的静态字段必须在类的头文件中“声明”,并在类的源文件中“实现”。

这是一个关于如何设置的简化示例。在这种情况下,在 BaseFactory.h 文件中,您应该声明静态字段:

class BaseFactory
{
private:
    static int map;
};

BaseFactory.cpp 文件中,实现了静态字段:

int BaseFactory::map = 392;

来自链接器的错误消息表明 BaseFactory::map 静态字段在 Derivedb.cpp 文件和 Switcher.cpp 中都已实现 文件。

即使实现 (... BaseFactory::map = ...) 不在任何一个文件中,如果将实现放在BaseFactory.h 头文件。 C++ 预处理器只是盲目地包含代码 header ,链接器无法判断实现是在 Switcher.cpp 文件中还是在 Switcher.cpp 包含的某个文件中。

就像方法需要在.h文件中声明并在.cpp文件中实现一样,静态字段也是如此。

关于C++ 工厂模式使用模板进行自注册,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7548002/

相关文章:

c++ - 创建相似对象时减少内存浪费

c++ - 是否可以将 Arduino Sketch 用作 C++ 程序?

c# - 将自定义格式添加到模板参数 Visual Studio 项目

java - 有没有等价于<?扩展 T>, <? C++中的 super T>?

c++ - 在内存中为多个文件保存构造的静态数组c++

C++11 原子。为什么这个编译,而不是链接?

android - 如何将 Celestia 构建到 Android?

C++ 模板 - 链表

gcc - 搜索和链接库目录的顺序

c++ - 骰子分布的直方图