Программирование >>  Разработка устойчивых систем 

1 ... 61 62 63 [ 64 ] 65 66 67 ... 196


Вместо this-> может использоваться любое допустимое уточнение, например, Sortable::at() или vector<T>::at(). Важно наличие уточнения, а не его конкретный вид.

чае обязательно (этот вопрос рассматривается в разделе Разрешение имен этой главы, а также в объяснении к примеру PriorityQueue6.cpp в главе 7).

Здесь приведена тестовая программа для файла Sortable.h, в которой используется генератор случайных чисел Urand (см. выше):

: C05:Sortable.cpp

{bor} (из-за присутствия bitset в Urand.h)

Тестирование специализированных шаблонов

#include <cstddef>

linclude <iostream>

linclude Sortable.h

linclude Urand.h

using namespace std:

Idefine asz(a) (sizeof a / sizeof a[0])

char* words[] - { is , running , big , dog , a . }: char* words2[] - { this , that , theother . }:

int mainO { Sortable<int> is: Urand<47> rand:

for(size t i = 0: i < 15: ++i)

is.push back(rnd()): for(size t i - 0: i < is.sizeO: ++i)

cout is[i] : cout endl: is.sortO:

for(size t i = 0: i < is.sizeO: ++i)

cout is[i] : cout endl:

Использование частичной специализации шаблона:

Sortable<string*> ss:

for(size t i = 0: i < asz(words): ++i)

ss.push back(new string(words[i])): for(size t i = 0: i < ss.sizeO: ++i)

cout *ss[i] : cout endl: ss.sortO:

for(size t i - 0: i < ss.sizeO: i++) { cout *ss[i] : delete ss[i]:

cout endl:

Использование полной специализации char*:

Sortable<char*> scp:

for(size t i = 0: i < asz(words2): ++i)

scp.push back(words2[i]): for(size t i - 0: i < scp.sizeO: ++i)

cout scp[i] : cout endl: scp.sortO;

for(size t i = 0: i < scp.sizeO: ++i)



cout scp[i] : cout endl; } III:-

Bo всех специализациях задействованы разные версии шаблона: Sortable<int> использует основной шаблон, а Sortable<string*> - неполную специализацию для указателей, Sortable<char*> - полную специализацию для char*. Без этой полной специализации у вас могло бы сложиться обманчивое впечатление, будто все работает правильно - массив words правильно сортируется в строку а big dog is running*, поскольку частичная специализация ограничивается сравнением первых символов массивов. Однако на примере массива words2 такая сортировка уже не работает.

Ограничение объема генерируемого кода

Каждый раз, когда в программе определяется специализация шаблона класса, компилятор генерирует код из определения класса для конкретной специализации вместе со всеми функциями, вызываемыми в программе. При этом генерируются только те функции класса, которые вызываются в программе. И это вполне логично, как показывает следующий пример: : С05:Delayed Instantiation.срр

Генерируется только код используемых функций шаблонов классов.

class X { public: void f() {}

class Y { public: void gO {}

template <typename T> class Z {

T t: public:

void aO { t.fO: }

void b() { t.gO: }

int mainO { Z<X> zx:

zx.aO: Код Z<X>::b() не генерируется Z<Y> zy:

zy.bO: Код Z<Y>::a() не генерируется

} III:-

Хотя шаблон Z вроде бы использует обе функции шаблона Т (f() и д()), программа успешно компилируется, а это означает, что код Z<X>::a() генерируется только при явном вызове этой функции для zx (если бы при этом также генерировался код Z<X>::b(), то компилятор выдал бы сообщение об ошибке вызова несуществующей функции Х::д()). Аналогично, вызов zy.b() не генерирует Z<Y>::a(). Следовательно, шаблон Z может использоваться для обоих классов (X и Y). Если бы все функции класса генерировались непосредственно при специализации, это бы существенно ограничило возможности применения многих шаблонов.



Допустим, у нас имеется шаблонный контейнер Stack, и в программе используются специализации int, int* и char*. В результате три версии кода Stack будут сгенерированы компилятором и скомпонованы с вашей программой. Как отмечалось ранее, шаблоны нужны прежде всего для того, чтобы избежать ручного дублирования кода; однако код все равно дублируется, просто компилятор генерирует его за вас. Основную часть реализации для указателей можно выделить в единый класс, используя комбинацию полной и частичной специализации. Идея заключается в том, чтобы определить полную специализацию для void*, а затем создать неполные специализации для остальных типов; это позволит применять общий код для разных типов. Следующий пример демонстрирует эту методику:

: C05:Nobloat.h

Совместное использование кода при хранении указателей в стеке

#ifndef NOBLOAT H

#define NOBLOAT H

#inc1ude <cassert>

#inc1ude <cstddef>

#1nc1ude <cstring>

Основной шаблон tempiate<cl ass T> class Stack { T* data:

std::s1ze t count: std::size t capacity: enum {INIT = 5}: public: StackО {

count = 0:

capacity = INIT:

data = new T[INIT]:

void pushCconst T& t) { if (count == capacity) { Выделение дополнительной памяти std::size t newCapacity = 2*capacity: T* newData = new T[newCapacity]: for (size t i = 0: i < count: ++i)

newData[i] = data[i]: delete [] data: data = newData: capacity = newCapacity:

assertCcount < capacity): data[count++] = t:

void popO { assertCcount > 0): --count;

T topO const { assertCcount > 0): return data[count-l]:

std::size t SizeO const { return count: }



1 ... 61 62 63 [ 64 ] 65 66 67 ... 196

© 2006 - 2024 pmbk.ru. Генерация страницы: 0.003
При копировании материалов приветствуются ссылки.
Яндекс.Метрика