В примере 8.12 определение шаблона дается в том же месте, что и его объявление. Обычно это делается для экономии места, занимаемого примером, не в данном случае есть и еще одна причина. Шаблоны (классов или функций — см. рецепт 8.12) компилируются в двоичную форму только тогда, когда создается их экземпляр. Таким образом, невозможно создать объявление шаблона в заголовочном файле, а его реализацию — в исходном файле (т.е. .cpp) Причина заключается в том, что в нем нечего компилировать! Из этого правила имеются исключения, но обычно при написании шаблона класса его реализация должна помешаться в заголовочном файле или встраиваемом файле, который подключается заголовочным.

В этом случае требуется использовать несколько необычный синтаксис. Методы и другие части класса объявляются как в обычном классе, но при определении методов требуется включить дополнительные лексемы, которые говорят компилятору, что это части шаблона класса. Например, getVal можно определить вот так (сравните с примером 8.12)

template

const T& TreeNode::getVal() const {

 return(val_);

}

Тело функции выглядит точно так же.

Однако с шаблонами следует быть осторожными, так как если написать шаблон, который используется повсеместно, то можно получить раздувание кода, что случается, когда один и тот же шаблон с одними и теми же параметрами (например, TreeNode) компилируется в нескольких объектных файлах. По существу в нескольких файлах окажется одно и то же двоичное представление экземпляра шаблона, и это сделает библиотеку или исполняемый файл значительно больше по размеру, чем требуется.

Одним из способов избежать этого является использование явного создания экземпляров, что позволяет указать компилятору создать версию шаблона класса для определенного набора аргументов шаблона. Если сделать это в таком месте, которое компонуется вместе с остальными клиентскими частями, то раздувания кода не произойдет. Например, если известно, что в приложении будет использоваться TreeNode, то в общий исходный файл можно поместить такую строку.

// common.cpp

template class TreeNode;

Соберите динамическую библиотеку с этим файлом, и после этого код, использующий TreeNode, сможет применять эту библиотеку динамически, не содержа своей собственной скомпилированной версии шаблона. Другой код может включить заголовочный файл шаблона класса, затем скомпоноваться с этой библиотекой и. следовательно, избежать необходимости иметь свою копию. Однако этот подход требует проведения экспериментов, так как не все компиляторы имеют одинаковые проблемы с раздуванием кода, но это общий подход для его минимизации.

Шаблоны C++ (как классов, так и функций) — это очень обширная тема, и имеется огромное количество методик создания мощных, эффективных проектов на основе шаблонов. Великолепным примером шаблонов классов являются контейнеры из стандартной библиотеки, такие как vector, list, set и другие, которые описываются в главе 15. Большая часть интересных разработок, описанных в литературе по С++, связана с шаблонами. Если вы заинтересовались этим предметом, почитайте группы новостей comp.lang.std.c++ и comp.lang.c++. В них всегда можно найти интересные вопросы и ответы на них.

Смотри также

Рецепт 8.12.

<p>8.12. Написание шаблона метода класса</p>Проблема

Имеется один метод, который должен принимать параметр любого типа, и невозможно ограничиться каким-либо одним типом или категорией типов (используя указатель на базовый класс).

Решение

Используйте шаблон метода и объявите параметр шаблона для типа объекта. Небольшая иллюстрация приведена в примере 8.13.

Пример 8.13. Использование шаблона метода

class ObjectManager {

public:

 template T* gimmeAnObject();

 template

 void gimmeAnObject(T*& p);

};

template

T* ObjectManager::gimmeAnObject() {

 return(new T);

}

template

void ObjectManager::gimmeAnObject(T*& p) {

 p = new T;

}

class X { /*...*/ };

class Y { /* ... */ };

int main() {

 ObjectManager om;

 X* p1 = om.gimmeAnObject(); // Требуется указать параметр

 Y* p2 = om.gimmeAnObject(); // шаблона

 om.gimmeAnObject(p1); // Однако не здесь, так как компилятор может

 om.gimmeAnObject(p2); // догадаться о типе T по аргументам

}

Обсуждение
Перейти на страницу:

Похожие книги