Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
OOP_otvety_k_ekzamenu.doc
Скачиваний:
55
Добавлен:
13.04.2015
Размер:
786.94 Кб
Скачать

Шаблоны классов

Аналогично функциям, классы также можно параметризовать относительно одного или нескольких типов. При помощи шаблонов классов удобно реализуются универсальные структуры данных. Как и в шаблоне функции, объявлению класса должна предшествовать часть template< typename T>со списком аргументов. Аргументов также может быть несколько.

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

#include <iostream>

template< typename T>

class Test

{

public:

void f ( T x )

{

// Вообще-то, не факт, что переменную типа T можно разыменовать!

* x = 5;

}

void g ()

{

std::cout << "Saying hello!" << std::endl;

} };

int main ()

{

// Создаем экземпляр шаблона класса с типом int.

// Разыменовывать тип int, как требует функция f, нельзя,

// но все прекрасно работает, потому что мы не вызываем функцию f!

Test< int > t;

t.g(); }

Каждый инстанцированный вариант шаблона класса - это отдельный класс. Несмотря на порождение от одного и того же источника, типы Test<int> и Test<short> - это разные классы, их нельзя приравнивать друг другу.

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

#include <iostream>

template< typename T >

class Test

{

public:

static int ms_objectCounter;

public:

Test () { ++ ms_objectCounter; }

Test ( const Test< T > & _t ) { ++ ms_objectCounter; }

};

int Test< int >::ms_objectCounter;

int Test< short >::ms_objectCounter;

int main ()

{

Test< int > ti1;

Test< int > ti2 = ti1;

std::cout << Test< int >::ms_objectCounter << std::endl;

std::cout << Test< short >::ms_objectCounter << std::endl;

}

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

template< typename T = int >

class Test

{

// ...

};

Ниже приведен полный пример полезного класса-шаблона для обобщенного АТД “стек” фиксированного размера. Отметим несколько основных правил написания шаблонов классов:

  1. При определении шаблона класса может возникнуть путаница с использованием его имени внутри определения. Когда контекст требует использовать имя класса, например, чтобы задать конструктор, оно указывается как обычно:

// Конструктор

Stack ( int _size = 10 );

Когда же речь идет о классе как о типе, рекомендуется явно указывать его в обобщенном виде с указанием аргумента шаблона:

// Оператор копирующего присвоения

Stack< T > & operator = ( const Stack< T >& _s );

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

template< typename T >

Stack< T >::Stack ( int _size )

: m_size( _size )

{

m_pData = new T[ m_size ];

m_pTop = m_pData;

}

  1. Чаще всего тела методов шаблонов классов размещают непосредственно в заголовочном файле после объявления класса. Это работает корректно, даже если функции не объявляются как встраиваемые (inline). CPP-файла для шаблона-класса чаще всего не создают вообще. Именно так выглядит практически весь код стандартной библиотеки шаблонов. Такой стиль реализации, не свойственный обычным классам C++, обуславливается особенностями компоновки шаблонов. Пока примем это как утверждение без объяснения, а детально разъясним позже.

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

void push ( const T& _value );

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

template class MyTemplate< int >;

template class MyTemplate< std::string >;

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

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

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]