Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Visual C++/MFC/WTL > Шаблоны в Visual C++


Автор: Dark Elf 31.1.2005, 13:00
Здравствуйте все!

У меня тут такая проблема: я объявляю шаблонный класс в Visual C++ пишу конструктор: в файле .h:
Код

CArray2D(T* source, unsigned width, unsigned height);
virtual ~CArray2D();

Потом пишу в файле .cpp:
Код

template <class T> CArray2D<T>::CArray2D(T* source, unsigned w, unsigned h)
{
width = w;
height = h;
unsigned size = width * height;
data = new T[size];
for (unsigned index = 0;index < size;index++)
{
 data[index] = source[index];
}
}
template <class T> CArray2D<T>::~CArray2D()
{
if (data)
{
 delete[] data;
}
}

При компиляции ошибок никаких не выдается, но при попытке создать экземпляр данного класса вот так:
Код

CArray2D <int> arr(f,5,5);

выдаются следующие ошибки линковки:
unresolved external symbol "public: virtual __thiscall CArray2D<int>::~CArray2D<int>(void)" (??1?$CArray2D@H@@UAE@XZ)
unresolved external symbol "public: __thiscall CArray2D<int>::CArray2D<int>(int *,unsigned int,unsigned int)" (??0?$CArray2D@H@@QAE@PAHII@Z)

Причём тут __thiscall мне вообще непонятно - это ж ключевое слово зарезервировано в VC6 для будущего использования и его использование вызывает ошибку...

Заранее благодарен за помощь

Автор: maxim1000 31.1.2005, 13:04
описание конструктора и создание экземпляра класса - в одном cpp или в разных?

Автор: Dark Elf 31.1.2005, 16:33
В разных .cpp - в частности создание экземпляра класса в функции main... это может что-то менять?
(заголовок "Array2D.h" естественно подключён smile)

Автор: bel_nikita 31.1.2005, 16:46
Объявляй и определяй все в *.H

Автор: chipset 31.1.2005, 16:58
Шаблоны сделаны так что реализацию нельзя отделять от обьявления. Это значит что все надо обьявлять в *.h.
Я кстати тоже споткнулся очень больно об это один раз ;)

Автор: maxim1000 31.1.2005, 17:06
да, может...
как работают шаблоны:
1. программист пишет (неважно где: в cpp или в h-файле, все равно в конце концов все оказывается в cpp) параметризированный код
этот параметризированный код НЕ КОМПИЛИРУЕТСЯ
2. компилятор смотрит, CArray2D каких типов нужны и для каждого такого случая сам пишет код в cpp-файл, подставляя нужный тип
таким образом, если, к примеру, основную часть размера программы составляет реализация массива (а используется он только для int), то при написании одной простой строки Array<char> размер программы увеличится приблизительно в два раза...
3. ну и еще одно важное замечание: все cpp-файлы компилируются ОТДЕЛЬНО
теперь ставим себя на место компилятора:
1. компилирует он файл с реализацией шаблона, смотрит, а, собственно, для каких типов тебе шаблон нужен, и не находит (в этом cpp он ни для какихтипов не нужен), вот он и не генерирует абсолютно никакого кода
2. компилирует он cpp-файл, в котором идет использование шаблона смотрит, там шаблон описан (h-файл ведь подключается), значит, он должен быть где-то снаружи, делает ссылку, которая указывает на несуществующий код, что и замечает линковщик

как починить (два способа):
1. создать переменную типа CArray2D <int> в cpp-файле, содержащем реализацию шаблона, это приведет к появлению нужного кода, и ссылка в другом файле уже не будет "в никуда"
2. разместить реализацию шаблона в h-файле
вообще-то я считаю размещение кода в h-файле дурным тоном, но есть два случая, когда я готов с ним мириться: макросы и шаблоны

какие отличия между этими способами:
(1) выглядит очень криво: для каждой спецификации шаблона нужно будет описывать переменную этого типа в соответствующем файле, это означает, что при каждом новом способе использования шаблона придется лезть в этот файл и добавлять что-то там... это очень плохо...
(2) выглядит очень привлекательно: для того, чтобы использовать шаблон, достаточно просто подключить соответствующий h-файл и наслаждаться реализованной кем-то функциональностью

НО: есть одна деталь: почему не очень хорошо помещать код в h-файл?
очень просто - h-файл вставляется в каждый cpp-файл, в котором будет написано #include, а это означает, что и компилироваться он будет каждый раз, вот и получается, что каждый файл, который использует Array<int>, на самом деле сам же его и реализует (подключением h-файла), т.е. один и тот же код будет присутствовать в куче obj-файлов после компиляции... это плохо, если, к примеру, реализация составляет ощутимую часть размера приложения... однако, вполне возможно, что создатели компиляторов (а точнее, линковщиков) все это уже продумали, и в результате остается только одна копия кода... как-нибудь на досуге проверю...
Добавлено @ 17:09
ого... тут уже наотвечали, пока я писал... все равно, надеюсь, это окажется полезным smile

Автор: maxim1000 31.1.2005, 18:08
небольшое дополнение:
Цитата
однако, вполне возможно, что создатели компиляторов (а точнее, линковщиков) все это уже продумали, и в результате остается только одна копия кода... как-нибудь на досуге проверю...

небольшой досуг наступил несколько минут назад: действительно, все продумано за нас - в конце концов остается только одна копия кода даже при включении в h-файл
так что теперь я смело могу объявить способ (1) - безграмотным и советовать ним не пользоваться smile

Автор: Dark Elf 31.1.2005, 22:18
Большое спасибо всем ответившим, особенно maxim1000

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)