Лекции.Орг


Поиск:




Категории:

Астрономия
Биология
География
Другие языки
Интернет
Информатика
История
Культура
Литература
Логика
Математика
Медицина
Механика
Охрана труда
Педагогика
Политика
Право
Психология
Религия
Риторика
Социология
Спорт
Строительство
Технология
Транспорт
Физика
Философия
Финансы
Химия
Экология
Экономика
Электроника

 

 

 

 


BoolStack< T, SIZE >::isEmpty () const




{

return m_pTop == m_Data;

}

 

//*****************************************************************************

 

// Реализация метода определения заполненности стека

template < typename T, int SIZE >

BoolStack< T, SIZE >::isFull () const

{

return (m_pTop - m_Data) == SIZE;

}

 

//*****************************************************************************

 

#endif // _STACK_FIXED_ARRAY_HPP_

 

 

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

 

Компоновка шаблонов

 

Размещение реализаций методов классов-шаблонов в заголовочных файлах обуславливается особенностями их компиляции и компоновки. Как было указано ранее, не вызванный нигде метод шаблонного класса не инстанцируется, даже если инстанцируется сам класс..

 

Такая модель делает непригодным классический вариант размещения кода шаблона класса в паре заголовочный файл - файл реализации. Представим процесс на следующем примере. Пусть имеется простейший шаблон, объявление которого помещено в заголовочный файл, а реализация методов - в CPP-файл. При этом, клиентский код, использующий данную абстракцию находится в отдельном от шаблона CPP-файле:

 

mytemplate.hpp

 

#ifndef _MYTEMPLATE_HPP_

#define _MYTEMPLATE_HPP_

 

//*****************************************************************************

 

template < typename T >

class MyTemplate

{

T * m_pData;

const int m_size;

public:

MyTemplate (int _size);

~MyTemplate ();
};

 

//*****************************************************************************

 

#endif // _MYTEMPLATE_HPP_

 

mytemplate.cpp

 

#include "mytemplate.hpp"

 

//*****************************************************************************

 

template < typename T >

MyTemplate< T >::MyTemplate (int _size)

: m_size(_size)

{

m_pData = new T[ m_size ];

}

 

//*****************************************************************************

 

template < typename T >

MyTemplate< T >::~MyTemplate ()

{

delete[] m_pData;

}

 

//*****************************************************************************

 

test.cpp

 

#include "mytemplate.hpp"

 

//*****************************************************************************

 

int main ()

{

MyTemplate< int > o(10);
}

 

//*****************************************************************************

 

Компиляция такой программы проходит успешно, в отличие от компоновки. Компоновщик выдаст загадочную на первый взгляд ошибку - будто бы отсутствует определение конструктора и деструктора:

 

error LNK2019: unresolved external symbol "public: __thiscall MyTemplate<int>::~MyTemplate<int>(void)" (??1?$MyTemplate@H@@QAE@XZ) referenced in function _main

error LNK2019: unresolved external symbol "public: __thiscall MyTemplate<int>::MyTemplate<int>(int)" (??0?$MyTemplate@H@@QAE@H@Z) referenced in function _main

 

Если задуматься, то такая ошибка является абсолютно логичной. Файлы test.cpp и mytemplate.cpp компилируются отдельными сеансами обработки. Каждый из этих исходных файлов должен породить собственный объектный файл. Выполняется следующий сценарий:

1. В файле mytemplate.cpp шаблон никак не инстанцируется, соответственно объектный файл не будет содержать машинного кода для определенных в нем методов (ведь они же никому не нужны в этом файле).

2. Файл test.cpp, включив заголовочный файл mytemplate.hpp, инстанцирует шаблон и вызывает искомые конструктор (явно) и деструктор (неявно). В данном объектном файле их нет, но компилятор не обращает на это никакого внимания, ведь поиск тел функций - задача этапа компоновки.

3. Компоновщик начинает работу, и не находит тел конструктора и деструктора, т.к. в месте инстанцирования тела не доступны, а в месте определения - тела не инстанцируются.

 

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

 

Альтернативой включению тел методов в заголовочные файлы является механизм явного инстанцирования (explicit instantiation). Если заранее известно с какими типами будет инстанцирован шаблон класса, то можно заранее искусственно инстанцировать тела всех методов в CPP-файле реализации. Например, известно ограничение, что шаблон MyTemplate будет инстанцироваться только с двумя типами - int и std::string. В таком случае в конце файла mytemplate.cpp следует добавить следующие директивы явного инстанцирования:

 

template class MyTemplate< int >;

template class MyTemplate< std::string >;

 

Увидев такие директивы, компилятор инстанцирует все известные ему тела методов шаблона MyTemplate с указанными типами, будет сгенерирован реальный машинный код, который попадет в объектный файл. В последствии, при использовании данного шаблона в другом CPP-файле, компоновщик без труда обнаружит подходящие функции.

 

Дополнительно, в С++11 был введен синтаксис внешнего явного инстанцирования, который помогает компилятору не делать повторяющиеся инстанцирования одного и того же шаблона в различных CPP-файлах:

 

extern template class MyTemplate< int >;

 

Такое объявление блокирует попытки неявного инстанцирования, как и обычное явное инстанцирование, однако, само по себе, не генерирует никакого машинного кода. Компилятор лишь получает информацию, что такой случай уже был инстанцирован где-либо еще в других CPP-файлах, при этом, конечный машинный код должен быть найден и состыкован на этапе компоновки, а в данном контексте можно сэкономить на времени компиляции, не делая лишней работы.

 

Модель явного инстанцирования подходит для шаблонов, используемых конечными прикладными программами, когда набор типов, с которыми производится инстанцирование, конечен и заранее известен. Такая модель совершенно не подходит для библиотек шаблонов, которые потенциально могут быть инстанцированы с любым подходящим под описание типом данных. В таких случаях доступна только модель включения - т.е. размещения тел методов в заголовочном файле.

 

До редакции стандарта С++’11 предусматривался третий способ организации компоновки шаблонов - ключевое слово export - однако с годами этот метод не прижился на практике, и ведущие производители компиляторов отказались от его реализации в виду слишком сложной схемы. В новой редакции стандарта этот механизм был объявлен устаревшим.

 





Поделиться с друзьями:


Дата добавления: 2017-01-21; Мы поможем в написании ваших работ!; просмотров: 356 | Нарушение авторских прав


Поиск на сайте:

Лучшие изречения:

Два самых важных дня в твоей жизни: день, когда ты появился на свет, и день, когда понял, зачем. © Марк Твен
==> читать все изречения...

2218 - | 2051 -


© 2015-2024 lektsii.org - Контакты - Последнее добавление

Ген: 0.012 с.