16.6. Вложенные типы шаблонов классов
16.6. Вложенные типы шаблонов классов
Шаблон класса QueueItem применяется только как вспомогательное средство для реализации Queue. Чтобы запретить любое другое использование, в шаблоне QueueItem имеется закрытый конструктор, позволяющий создавать объекты этого класса исключительно функциям-членам класса Queue, объявленным друзьями QueueItem. Хотя шаблон QueueItem виден во всей программе, создать объекты этого класса или обратиться к его членам можно только при посредстве функций-членов Queue.
Альтернативный подход к реализации состоит в том, чтобы вложить определение шаблона класса QueueItem в закрытую секцию шаблона Queue. Поскольку QueueItem является вложенным закрытым типом, он становится недоступным вызывающей программе, и обратиться к нему можно лишь из шаблона класса Queue и его друзей (например, оператора вывода). Если же сделать члены QueueItem открытыми, то объявлять Queue другом QueueItem не понадобится.
Семантика исходной реализации при этом сохраняется, но отношение между шаблонами QueueItem и Queue моделируется более элегантно.
Поскольку при любой конкретизации шаблона Queue требуется конкретизировать тем же типом и QueueItem, то вложенный класс должен быть шаблоном. Вложенные классы шаблонов сами являются шаблонами классов, а параметры объемлющего шаблона можно использовать во вложенном:
template class Type
class Queue:
// ...
private:
class QueueItem {
public:
QueueItem( Type val )
: item( val ), next( 0 ) { ... }
Type item;
QueueItem *next;
};
// поскольку QueueItem - вложенный тип,
// а не шаблон, определенный вне Queue,
// то аргумент шаблона Type после QueueItem можно опустить
QueueItem *front, *back;
// ...
};
При каждой конкретизации Queue создается также класс QueueItem с подходящим аргументом для Type. Между конкретизациями шаблонов QueueItem и Queue имеется взаимно однозначное соответствие.
Вложенный в шаблон класс конкретизируется только в том случае, если он используется в контексте, где требуется полный тип класса. В разделе 16.2 мы упоминали, что конкретизация шаблона класса Queue типом int не означает автоматической конкретизации и класса QueueItemint. Члены front и back - это указатели на QueueItemint, а если объявлены только указатели на некоторый тип, то конкретизировать соответствующий класс не обязательно, хотя QueueItem вложен в шаблон класса Queue. QueueItemint конкретизируется только тогда, когда указатели front или back разыменовываются в функциях-членах класса Queueint.
Внутри шаблона класса можно также объявлять перечисления и определять типы (с помощью typedef):
template class Type, int size
class Buffer:
public:
enum Buf_vals { last = size-1, Buf_size };
typedef Type BufType;
BufType array[ size ];
// ...
}
Вместо того чтобы явно включать член Buf_size, в шаблоне класса Buffer объявляется перечисление с двумя элементами, которые инициализируются значением параметра шаблона. Например, объявление
Bufferint, 512 small_buf;
устанавливает Buf_size в 512, а last - в 511. Аналогично
Bufferint, 1024 medium_buf;
устанавливает Buf_size в 1024, а last - в 1023.
Открытый вложенный тип разрешается использовать и вне определения объемлющего класса. Однако вызывающая программа может ссылаться лишь на конкретизированные экземпляры подобного типа (или элементов вложенного перечисления). В таком случае имени вложенного типа должно предшествовать имя конкретизированного шаблона класса:
// ошибка: какая конкретизация Buffer?
Buffer::Buf_vals bfv0;
Bufferint,512::Buf_vals bfv1; // правильно
Это правило применимо и тогда, когда во вложенном типе не используются параметры включающего шаблона:
template class T class Q {
public:
enum QA { empty, full }; // не зависит от параметров
QA status;
// ...
};
#include iostream
int main() {
Qdouble qd;
Qint qi;
qd.status = Q::empty; // ошибка: какая конкретизация Q?
qd.status = Qdouble ::empty; // правильно
int val1 = Qdouble ::empty;
int val2 = Qint ::empty;
if ( val1 != val2 )
cerr "ошибка реализации!" endl;
return 0;
}
Во всех конкретизациях Q значения empty одинаковы, но при ссылке на empty необходимо указывать, какому именно экземпляру Q принадлежит перечисление.
Упражнение 16.8
Определите класс List и вложенный в него ListItem из раздела 13.10 как шаблоны. Реализуйте аналогичные определения для ассоциированных членов класса.
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОКЧитайте также
Типы, характеризуемые значениями, ссылочные типы и оператор присваивания
Типы, характеризуемые значениями, ссылочные типы и оператор присваивания Теперь изучите следующий метод Main() и рассмотрите его вывод, показанный на рис. 3.12.static void Main(string[] args) { Console.WriteLine("*** Типы, характеризуемые значением / Ссылочные типы ***"); Console.WriteLine(-› Создание p1"); MyPoint
Типы, характеризуемые значениями и содержащие ссылочные типы
Типы, характеризуемые значениями и содержащие ссылочные типы Теперь, когда вы чувствуете разницу между типами, характеризуемыми значением, и ссылочными типами, давайте рассмотрим более сложный пример. Предположим, что имеется следующий ссылочный тип (класс),
Типы, характеризуемые значениями, и ссылочные типы: заключительные замечания
Типы, характеризуемые значениями, и ссылочные типы: заключительные замечания Чтобы завершить обсуждение данной темы, изучите информацию табл. 3.8, в которой приводится краткая сводка основных отличий между типами, характеризуемыми значением, и ссылочными типами.Таблица
ВЛОЖЕННЫЕ ЦИКЛЫ
ВЛОЖЕННЫЕ ЦИКЛЫ Вложенным называется цикл, находящийся внутри другого цикла. В этом разделе рассматривается пример, в котором вложенные циклы используются для нахождения всех простых чисел, не превышающих данного значения. Простое число - это такое число, которое
13.10. Вложенные классы A
13.10. Вложенные классы A Класс, объявленный внутри другого класса, называется вложенным. Он является членом объемлющего класса, а его определение может находиться в любой из секций public, private или protected объемлющего класса.Имя вложенного класса известно в области видимости
16.1.1. Определения шаблонов классов Queue и QueueItem
16.1.1. Определения шаблонов классов Queue и QueueItem Ниже представлено определение шаблона класса Queue. Оно помещено в заголовочный файл Queue.h вместе с определением шаблона QueueItem:#ifndef QUEUE_H#define QUEUE_H// объявление QueueItemtemplate class T class QueueItem;template class Typeclass Queue {public:Queue() : front( 0 ), back ( 0 ) { }~Queue();Type&
16.3. Функции-члены шаблонов классов
16.3. Функции-члены шаблонов классов Как и для обычных классов, функция-член шаблона класса может быть определена либо внутри определения шаблона (и тогда называется встроенной), либо вне его. Мы уже встречались со встроенными функциями-членами при рассмотрении шаблона Queue.
16.9. Специализации шаблонов классов A
16.9. Специализации шаблонов классов A Прежде чем приступать к рассмотрению специализаций шаблонов классов и причин, по которым в них может возникнуть надобность, добавим в шаблон Queue функции-члены min() и max(). Они будут обходить все элементы очереди и искать среди них
16.10. Частичные специализации шаблонов классов A
16.10. Частичные специализации шаблонов классов A Если у шаблона класса есть несколько параметров, то можно специализировать его только для одного или нескольких аргументов, оставляя другие неспециализированными. Иными словами, допустимо написать шаблон, соответствующий
Вложенные транзакции
Вложенные транзакции В Firebird транзакции всегда запускаются и завершаются клиентом. Некоторые другие СУБД могут запускать и подтверждать транзакции из хранимых процедур, потому что для управления транзакциями они используют двухфазную блокировку транзакций. Вместо
10.2. Вложенные циклы
10.2. Вложенные циклы Цикл называется вложенным, если он размещается внутри другого цикла. На первом проходе, внешний цикл вызывает внутренний, который исполняется до своего завершения, после чего управление передается в тело внешнего цикла. На втором проходе внешний цикл
18.5.11. Вложенные циклы for
18.5.11. Вложенные циклы for Чтобы вложить, циклы, достаточно цикл for поместить в другой цикл:for имя_переменной in listdofor имя_переменной2 in list2doкоманда1done doneВ следующем сценарии представлен вложенный цикл for. Имеются два списка, APPS и SCRIPTS, первый из которых содержит путь к