Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Линковщик не находит реализацию функции класса  (Прочитано 19313 раз)
0 Пользователей и 10 Гостей смотрят эту тему.
andrystepa
Помогающий

ru
Offline Offline

« : 27-06-2007 18:21 » 

Использую Visual Studio 2005. Проблема такая: создал шаблон класса список. В класс включил функцию упорядочивания списка. Пишу консольную программу, для тестирования класса. Даю команду Build и получаю ошибку линкера:
unresolved external symbol "public: void __thiscall ListTpl<int>::LstOrder(void)" (?LstOrder@?$ListTpl@H@@QAEXXZ) referenced in function _main
1>C:\Programming\Visual Studio 2005\Projects\LstConcat\Debug\LstConcat.exe : fatal error LNK1120: 1 unresolved externals...

Вроде все заголовочные файлы включены.
Попробовал реализацию функции вставить в заголовочный файл - все нормально работает. Может дело в настройках проекта? Где-то надо дать команду линкеру включить файл реализации класса? Но где? Я не силен в настройках проекта Visual Studio.
Содержимое заголовочного файла класса такое:
Код:
#include<iostream>
#include "NodeTpl.h"
using namespace std;

template<class T>
class ListTpl {
public:
ListTpl():head(NULL) { };
ListTpl( const T& first ) { head=new NodeTpl<T>*(first, NULL); };
void addHead( const T& data) { head=new NodeTpl<T>(data, head); };
void insert(const T& data, NodeTpl<T>* afterme) {
afterme->setlink(new NodeTpl(data, (afterme->getlink());); );
}
void delNode(NodeTpl<T>* afterme) {
NodeTpl<T>* toDel=(afterme->getlink(););
afterme->setlink(toDel->getlink(););
delete toDel;
}
void showLst() {
NodeTpl<T>* current=head;
while(current!=NULL) {
cout<<(current->getdata())<<endl;
current=(current->getlink());
};
}

void LstOrder(); /* {
NodeTpl<T>* tmp1=head;
while(tmp1!=NULL) {
NodeTpl<T>* tmp2=tmp1->getlink();
//NodeTpl<T>* before=tmp1;
while(tmp2!=NULL) {
if((tmp1->getdata())>(tmp2->getdata())) {
T olddata=tmp1->getdata();
tmp1->setdata(tmp2->getdata());
tmp2->setdata(olddata);
};
tmp2=tmp2->getlink();
};
tmp1=tmp1->getlink();
};*/

NodeTpl<T>* findNode(T& toFind, NodeTpl<T>* head);

private:
NodeTpl<T>* head;
};


Реализация функции LstOrder тут закомментирована. Если ее раскомментировать все заработает.
А файл реализации класса:
Код:
#include "ListTpl.h"

template<class T>
NodeTpl<T>* ListTpl<T>::findNode(T& toFind, NodeTpl<T>* head) {
NodeTpl<T>* curr=head;
while(curr!=NULL) {
if((curr->getdata();)==toFind) return curr;
curr=curr->getlink();
};
return NULL;
}

template<class T>
void LstOrder() {
NodeTpl<T>* tmp1=head;
while(tmp1!=NULL) {
NodeTpl<T>* tmp2=tmp1->getlink();
//NodeTpl<T>* before=tmp1;
while(tmp2!=NULL) {
if(tmp1>tmp2) {
T olddata=tmp1->getdata();
tmp1->setdata(tmp2->getdata(););
tmp2->setdata(olddata);
};
tmp2=tmp2->getlink();
};
tmp1=tmp1->getlink();
};
}

В тестовой программе эта функция вызывается так:
Код:
#include<iostream>
#include "ListTpl.h"
using namespace std;

int main() {
......
//Создаем экземпляр класса и инициализируем его
.....
        Mylist.showLst();
cout<<"Упорядочиваем!"<<endl;
Mylist.LstOrder();
cout<<"Получили: "<<endl;
Mylist.showLst();
...


Подскажите, в чем проблема? Может дело в опциях линкера?
Записан
sss
Специалист

ru
Offline Offline

« Ответ #1 : 28-06-2007 01:17 » 

У тебя функция

Код:
template<class T>
void LstOrder() {
NodeTpl<T>* tmp1=head;
while(tmp1!=NULL) { ....

не является членом класса.  Надо

Код:
template<class T>
void ListTpl<T>::LstOrder() {
NodeTpl<T>* tmp1=head;
while(tmp1!=NULL) { ....

Записан

while (8==8)
Антон (LogRus)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #2 : 28-06-2007 05:13 » 

andrystepa, всё очнь просто. Если ты объявление шаблона оставляешь в хедере, а реализацию вытаскиеваешь в cpp, то ты в cpp файле должен делать явное инстанцирование шаблона. У тебя компилятор не будет компилировать шаблон до его первого использования, ты его конечно используешь, но в той еденице трансляции, где ты его используешь не видно тела поэтому компилятор делает только ссылки на реализцию, без компиляции реализации, а в единице трансляции, где у тебя есть тело шалона ты его не разу не используешь с нужным типом поэтому он в той еденице компиляции и не компилирется для нужного типа, поэтому и нужно явное инстанцирование.

Проблематика включения реализации в cpp или h хорошо описывается в книге "Шаблоны C++" Давида Вандервуда(Ундервуда) и Николая Джосьютиса(Джосатиса). Раздел 6.
Записан

Странно всё это....
andrystepa
Помогающий

ru
Offline Offline

« Ответ #3 : 28-06-2007 15:45 » 

У тебя функция

Код:
template<class T>
void LstOrder() {
NodeTpl<T>* tmp1=head;
while(tmp1!=NULL) { ....

не является членом класса.  Надо

Код:
template<class T>
void ListTpl<T>::LstOrder() {
NodeTpl<T>* tmp1=head;
while(tmp1!=NULL) { ....


Исправил как указано. Все равно при линковке выдает:
error LNK2019: unresolved external symbol "public: void __thiscall ListTpl<int>::LstOrder(void)" (?LstOrder@?$ListTpl@H@@QAEXXZ) referenced in function _main
1>C:\Programming\Visual Studio 2005\Projects\LstConcat\Debug\LstConcat.exe : fatal error LNK1120: 1 unresolved externals

andrystepa, всё очнь просто. Если ты объявление шаблона оставляешь в хедере, а реализацию вытаскиеваешь в cpp, то ты в cpp файле должен делать явное инстанцирование шаблона. У тебя компилятор не будет компилировать шаблон до его первого использования, ты его конечно используешь, но в той еденице трансляции, где ты его используешь не видно тела поэтому компилятор делает только ссылки на реализцию, без компиляции реализации, а в единице трансляции, где у тебя есть тело шалона ты его не разу не используешь с нужным типом поэтому он в той еденице компиляции и не компилирется для нужного типа, поэтому и нужно явное инстанцирование.

Проблематика включения реализации в cpp или h хорошо описывается в книге "Шаблоны C++" Давида Вандервуда(Ундервуда) и Николая Джосьютиса(Джосатиса). Раздел 6.

Прошу прощения, но термина инстанцирование я не знаю. Объясните пожалуйста, что он означает. И если можно, ссылку на упомянутую книгу, если эта ссылка существует, приведите пожалуйста.
Записан
sss
Специалист

ru
Offline Offline

« Ответ #4 : 29-06-2007 01:01 » 

Инстанцирование - момент формирования класса из шаблона.
Может у тебя в source список не добавлен файл ListTpl.cpp ?
Записан

while (8==8)
andrystepa
Помогающий

ru
Offline Offline

« Ответ #5 : 29-06-2007 06:57 » 

Инстанцирование - момент формирования класса из шаблона.
Может у тебя в source список не добавлен файл ListTpl.cpp ?
Я подозревал нечто подобное, но по Visual Studio у меня никаких книжек нет, и как добавлять файл в список исходников не знаю. Я просто добавляю файл в папку Source Files (или Header Files, в зависимости от того, какой файл) проекта и редактирую его. До этого никаких проблем небыло - все файлы находились и все компилировалось. Возможно, где-то есть опции настройки строки компиляции и линкования, но я не нашел.Но в g++ я все скомпилил (там, правда все исходники в командной строке указываются).
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #6 : 29-06-2007 07:14 » 

andrystepa, в VC есть панелька WorkSpace с деревьями ресурсов, классов и файлов проекта. Вот в дерефо файлов проекта и надо добавить (щёлкай правой кнопкой по корню дерева (или по той папке дерева,куда надо файл добавить) и добавляй )
По крайней мере в VC6 так
Записан

Антон (LogRus)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #7 : 29-06-2007 07:40 » 

andrystepa, ссылку не знаю, могу кинуть в почту на английском версию. У меня дома бумажная на русском поэтому не заморачивался с электронной версией.

Что касается добавления файлов andrystepa, ты всё правильно делаешь.
Так что проблема только в том, что у тебя не инстанцирован(т.е. не скомпилирован код код шаблона для нужного тебе типа) шаблон, если шаблон не инстанцируется, то, он и не компилирется, по стандарту единственное что должен сделать компилятор проверить внутри тела синтаксис и типы который не зависит от параметров шаблона, но VC++ даже этого не делает(что иногда очень удобно), в отличии от gcc.

Записан

Странно всё это....
andrystepa
Помогающий

ru
Offline Offline

« Ответ #8 : 29-06-2007 08:52 » new

andrystepa, ссылку не знаю, могу кинуть в почту на английском версию. У меня дома бумажная на русском поэтому не заморачивался с электронной версией.

Что касается добавления файлов andrystepa, ты всё правильно делаешь.
Так что проблема только в том, что у тебя не инстанцирован(т.е. не скомпилирован код код шаблона для нужного тебе типа) шаблон, если шаблон не инстанцируется, то, он и не компилирется, по стандарту единственное что должен сделать компилятор проверить внутри тела синтаксис и типы который не зависит от параметров шаблона, но VC++ даже этого не делает(что иногда очень удобно), в отличии от gcc.
andrystepa,
Если можно, скинь на мейл книжку, пожалуйста, хотябы и на английском. А все-таки непонятно, что нужно сделать, чтобы код шаблона был скомпилирован?
Записан
Антон (LogRus)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #9 : 29-06-2007 10:38 » 

что делать я тебе сказал в конце cpp файла с телом шаблона выполнить явное инстанцирование.

для твоего класса явное инстанцирование для типа int выгдит так:
template ListTpl<int>;

Можно инстанцировать один метод.
template void ListTpl<int>::LstOrder();

а где товой мэил?
Записан

Странно всё это....
andrystepa
Помогающий

ru
Offline Offline

« Ответ #10 : 29-06-2007 10:50 » 

Мой мейл andrystepa@mail.ru . Прошу прощения, мне казалось что он виден в моем профиле. Возможно, я отключил его показ, и вижу только сам.
Одно мне непонятно, если этот класс будет применяться с другими типами данных, надо и их инстанцировать? Тогда получается, надо инстанцировать для всех типов данных, которые можно использовать с этим классом - то есть вставлять в файл реализации объявления шаблона со всеми допустимыми типами данных! Это не слишком удобно....
Записан
Антон (LogRus)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #11 : 29-06-2007 11:06 » 

andrystepa, ты абсалютно прав. Улыбаюсь
можно этот cpp включать в другие cpp и там делать инстанцирование
главный + такого подхода это отсутсвие компиляции шаблона в каждой еденици трансляции куда он включен.

Меня такой подход вполне устраивает в ряде случаев.
Книга в почте.
Записан

Странно всё это....
andrystepa
Помогающий

ru
Offline Offline

« Ответ #12 : 29-06-2007 11:59 » 

Благодарю за книженцию! А если этот .cpp включать в другие, то нафига нужен заголовочный файл?
Записан
Антон (LogRus)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #13 : 29-06-2007 14:57 » 

что бы обявить интерфейс

ну например тебе нужно ипользовать шаблон для 10 классов, если ты все зависимости(а они могут быть ОЧЕНЬ жирными вплоть до того что у тебя компилятор сглючит, сталкивался не однократно) для этих 10 классов засунешь в cpp будет не очень красиво


или такой вариант что шаблон настолько тяжелый, что каждый отдельный метод придётся инстанцировать в различных файлах

Записан

Странно всё это....
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines