Шаблон C ++ Factory с использованием шаблонов для самостоятельной регистрации - PullRequest
1 голос
/ 25 сентября 2011

Мои вопросы соответствуют ответу Йоханнеса в Есть ли способ создания экземпляров объектов из строки, содержащей их имя класса? и недавний комментарий от Спенсер Роуз. Поскольку я не могу добавить комментарий, я решил начать новый вопрос.

Мне нужно предложение Йоханнеса. Я реализовал это точно таким же образом, но у меня есть неразрешенная ошибка компоновщика внешних символов при использовании VS2008, которая, похоже, как-то связана с картой. Я пытаюсь со дня на день решить это. Сегодня я прочитал комментарий от Спенсера и добавил строку, которую он предлагает

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)

что означает, что я мог определить это дважды? Пожалуйста, могли бы Спенсер или кто-нибудь опубликовать улучшенный код base.hpp. Это настолько важное решение, что оно наверняка пригодится многим начинающим программистам на С ++.

Второй вопрос: -> Эта проблема решена! Спасибо!

Мне нужны объявления некоторых функций в base.hpp. Они должны были быть абстрактными в базовом классе и реализованы в подклассах (например, Derivedb.cpp). Но

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

в base.hpp выдал ошибку компилятора. Удаление "= 0" решило ошибку компилятора. Но теперь у меня есть еще один неразрешенный внешний символ 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).

Я называю это в другом файле cpp

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

Может быть, не ясно, какую функцию (базовый или подкласс) нужно вызывать, поскольку она не является абстрактной в базовом классе ??? Есть ли способ решить эту проблему? Спасибо!

Ответы [ 2 ]

1 голос
/ 26 сентября 2011

Из ошибки 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 ++ просто вслепую включает заголовки кода, и компоновщик не может определить, находится ли реализация в файле Switcher.cpp или в каком-либо файле, включенном Switcher.cpp.

Так же, как методы должны быть объявлены в .h файлов и реализовано в .cpp файлах, то же самое касается статических полей.

0 голосов
/ 26 сентября 2011

map - это имя класса шаблона в STL, и поэтому плохой выбор для имени переменной. Это может или не может быть связано с ошибкой определения дубликата, которую вы получаете.

...