Скачиваний:
42
Добавлен:
15.06.2014
Размер:
527.87 Кб
Скачать

21

Шаблоны классов со статическими членами

int clt<int>::Nobj = 0;

int clt<char*>::Nobj = 0; Результаты: int clt<double>::Nobj = 0;

void main()

{

clt<int> Num(5); clt<char*> Str("Message"); clt<double> dbl(5.9);

cout << Num << Str; Num.get()++; Str.get() = "Hi-hi"; cout << Num << Str; clt<int> Obj(12); cout << Obj << dbl; char s; cin >> s;

}

22

Наследование

Параметризованные классы могут быть производными от других классов, параметризованных или нет.

Параметризованный класс может выступать в качестве базового; в этом случае производный класс также будет

параметризованным с тем же форматом аргументов, что и в базовом классе.

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

с третьей строки…Чаще всего встречается наследование

параметризованного типа от непараметризованного.

23

Комбинации простых и параметризованных типов

Рассмотрим 3 ситуации:

1)Предположим, у вас имеется параметризованный класс, реализация всех функций которого занимает 1000 строк. При каждом его использовании для нового типа компилятор радостно выплевывает очередные 1000 строк расширенного кода. Это слишком высокая цена за безопасность типа.

2)Допустим, вы продаете библиотеку классов и не хотите поставлять исходный текст, а только интерфейсы. Если библиотека содержит параметризованные функции, они должны находиться в открытом для всего мира файле .h. Обидно.

3)Допустим, кто-то передает вам замечательный, но небезопасный по отношению к типам класс или библиотеку классов. Может быть, он был написан на компиляторе, который не поддерживает шаблоны, или автор просто не верит в шаблоны. Вам хочется подправить код и сделать его безопасным с помощью шаблонов. Но хотите ли вы переделывать все подряд, включать весь код реализации в файлы .h и добавлять в объявления класса параметры и символы <>?

24

Комбинации простых и параметризованных типов

Во всех описанных ситуациях стоит использовать параметризованный тип в

сочетании с простым, непараметризованным типом.

Когда это будет сделано, в 99 случаях из 100 параметризованный тип «заворачивает» простой тип в симпатичную, мягкую и безопасную по

отношению к типам оболочку.

При этом простой класс не изменяется — просто параметризованный класс помещается между небезопасным классом и пользователем.

25

Небезопасные типы в открытых базовых классах

Небезопасные типы в открытых базовых классах - это не

серьезно.

Если вы попытаетесь ввести безопасность типов в

производном классе с открытым наследованием, клиент получит полный доступ ко всем небезопасным средствам

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

(из книги Джефф Элджер. С++. Библиотека программиста)

Базовый класс недоступен для клиентов производного шаблона

26

Небезопасные типы в закрытых базовых классах

Самый простой способ обеспечить безопасность типов — сделать ненадежный класс закрытым базовым классом

безопасного шаблона.

class UnsafeNode { // ListNode из предыдущего примера

private: UnsafeNode* next; void* data; public:

UnsafeNode(void* d, UnsafeNode* n); virtual ~UnsafeNode();

UnsafeNode* Next(); void* Data();

};

template <class Type>

class SafeNode : private UnsafeNode { public:

SafeNode(Type* d, SafeNode* n) : UnsafeNode(d, n) {} virtual ~SafeNode() { delete (Type*)Data(); } SafeNode* Next() { return (SafeNode*)UnsafeNode::Next(); }

Type* Data() { return (Type*)UnsafeNode::Data(); }

27

Ключевое слово typename

Ключевое слово typename означает, что следующий

за ним идентификатор обозначает тип. Рассмотрим

template <class T> class MyClass { typename SubType * ptr;

. . .

};

В этом примере ключевое слово typename поясняет, что SubType

является подтипом класса T. Тогда ptr

– указатель на тип T::SubType.

Без typename идентификатор SubType интерпретируется как статический член класса, а следующая строка

как умножение значения SubTyp типа T на ptr: T::SubType * ptr . Поскольку идентификатор SubType объявлен типом, любой тип, используемый вместо T, должен иметь в себе внутренний тип

SubType.

 

 

MyClass <Q> x;

Например, использование типа Q в

// Объявление класса

качестве аргумента шаблона,

class Q {

SubType;

typedef int

 

. . .

 

только при условии, что в Q

};

 

 

внутренний тип SubType:

28

Еще о typename

В рассмотренном примере переменная ptr является указателем на int. В общем случае подтип может быть абстрактным типом данных (например классом). В этом случае:

class Q {

class SubType;

. . .

};

Ключевое слово typename всегда должно квалифицировать идентификатор шаблона как тип, даже если другая интерпретация не имеет смысла. В С++ любой идентификатор шаблона, указанный без ключевого слова typename, интерпретируется как значение.

Ключевое слово typename также может использоваться вместо слова class в определении шаблона:

template <typename Type> class clt {

. . .

};

29

Нетипизированные параметры шаблонов

В качестве параметров шаблонов могут выступать не

только типы. Однако такой нетипизированный параметр все равно считается частью определения типа.

Так например, аргумент шаблона стандартного класса bitset <> содержит количество входящих в него бит.

bitset <32> field32;

// поле на 32

бита

bitset <50> field50;

// поле на 50

бит

Эти поля будут относится к разным типам, поскольку при

их определении задаются разные значения аргументов

шаблона. Следовательно, их нельзя присваивать или сравнивать друг с другом (при отсутствии соответствующего

преобразования типа).

30

Параметры шаблонов по умолчанию

Шаблоны классов могут иметь параметры по умолчанию.

Следующий пример разрешает объявлять объекты класса MyClass как с одним, так и с двумя аргументами.

template <class T, class container= vector <T> > class MyClass . . .

При передаче одного аргумента вместо второго

используется параметр по умолчанию:

MyClass <int> x1; // Эквивалентно: MyClass <int, vector

<int> > x1;

Аргументы шаблонов по умолчанию могут определяться на основе предыдущих аргументов.