- Фантастика и фэнтези
- Ироническое фэнтези
- Стимпанк
- Детективная фантастика
- Киберпанк
- Попаданцы
- LitRPG
- Космоопера
- Технофэнтези
- Городская фантастика
- Русское фэнтези
- Юмористическая фантастика
- Космическая фантастика
- Боевое фэнтези
- Эпическая фантастика
- Зарубежная фантастика
- Книги магов
- Мистика
- Разная фантастика
- Разное фэнтези
- Любовное фэнтези
- Романтическая фантастика
- Городское фентези
- Историческая фантастика
- Историческое фэнтези
- Социально-философская фантастика
- Сказочная фантастика
- Иностранное фэнтези
- Романтическое фэнтези
- Ироническая фантастика
- Ужасы и Мистика
- Постапокалипсис
- Героическая фантастика
- Фэнтези
- Научная Фантастика
- Социально-психологическая
- Альтернативная история
- Боевая фантастика
- Разная литература
- Пословицы, поговорки
- Шахматы
- Визуальные искусства
- Фанфик
- Авто и ПДД
- Военное
- Отраслевые издания
- Гиды, путеводители
- Зарубежная образовательная литература
- Военная история
- Цитаты из афоризмов
- Современная литература
- Великолепные истории
- Начинающие авторы
- Современная зарубежная литература
- Боевые искусства
- Изобразительное искусство, фотография
- Кино
- Литература 19 века
- Недвижимость
- Музыка, музыканты
- Готические новеллы
- Истории из жизни
- Культура и искусство
- Спецслужбы
- Зарубежная прикладная литература
- Музыка, танцы
- Подростковая литература
- Прочее
- Военная техника, оружие
- Газеты и журналы
- Периодические издания
- Домоводство, Дом и семья
- Любовные романы
- Экономическая литература
- Научные и научно-популярные книги
- О животных
- Биохимия
- Культурология
- Юриспруденция
- Политика
- Психотерапия
- Языкознание
- Научпоп
- Медицина
- Психология, личное
- Радиотехника
- Филология
- Педагогика
- Образовательная литература
- Деловая литература
- Физика
- Науки: разное
- Химия
- Воспитание детей, педагогика
- Детская психология
- Зарубежная публицистика
- Биология
- Архитектура
- Зарубежная психология
- Науки о космосе
- Социология
- Математика
- География
- Беременность, ожидание детей
- Литературоведение
- Транспорт, военная техника
- Обществознание
- Зоология
- География
- Альтернативная медицина
- Иностранные языки
- Ветеринария
- Рефераты
- Астрология
- Биофизика
- Экология
- История Европы
- Учебники
- Шпаргалки
- Государство и право
- Ботаника
- Религиоведение
- История
- Техническая литература
- Прочая научная литература
- Психология
- Детективы и Триллеры
- Справочная литература
- Проза
- Новелла
- Юмор
- Проза
- Историческая проза
- Русская современная проза
- Советская классическая проза
- Повести
- Очерки
- Эссе
- Рассказы
- Зарубежная современная проза
- Сентиментальная проза
- Зарубежная классика
- Афоризмы
- Эпистолярная проза
- Феерия
- Семейный роман/Семейная сага
- Разное
- Антисоветская литература
- Магический реализм
- Русская классическая проза
- Современная проза
- О войне
- Контркультура
- Классическая проза
- Поэзия, Драматургия
- Детская литература
- Детская фантастика
- Детские остросюжетные
- Детские стихи
- Детские приключения
- Внеклассное чтение
- Учебная литература
- Зарубежные детские книги
- Бизнес
- Загадки
- Книги для подростков
- Школьные учебники
- Детский фольклор
- Книги для дошкольников
- Детские детективы
- Детская познавательная и развивающая литература
- Буквари
- Прочая детская литература
- Сказка
- Детская проза
- Детская образовательная литература
- Юмор
- Документальные книги
- Бизнес
- Внешнеэкономическая деятельность
- Тайм-менеджмент
- Кадровый менеджмент
- О бизнесе популярно
- Управление, подбор персонала
- Бизнес
- Менеджмент и кадры
- Продажи
- Экономика
- Ценные бумаги и инвестиции
- Малый бизнес
- Делопроизводство, офис
- Корпоративная культура, бизнес
- Банковское дело
- Финансы
- Зарубежная деловая литература
- Ораторское искусство / риторика
- Личные финансы
- Интернет-бизнес
- Поиск работы
- Государственное и муниципальное управление
- Бухучет и аудит
- Работа с клиентами
- Менеджмент
- Краткое содержание
- Личная эффективность
- Переговоры
- Приключения
- Старинная литература
- Религия и духовность
- Компьютеры и Интернет
- Блог
Читаем без скачивания Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп
Шрифт:
Интервал:
Закладка:
19.3.3. Контейнеры и наследование
Это одна из разновидностей сочетания объектно-ориентированного и обобщенного программирования, которое люди постоянно, но безуспешно пытаются применять: использование контейнера объектов производного класса в качестве контейнера объектов базового класса. Рассмотрим пример.
vector<Shape> vs;
vector<Circle> vc;
vs = vc; // ошибка: требуется класс vector<Shape>
void f(vector<Shape>&);
f(vc); // ошибка: требуется класс vector<Shape>
Но почему? “В конце концов, — говорите вы, — я могу конвертировать класс Circle в класс Shape!” Нет, не можете. Вы можете преобразовать указатель Circle* в Shape* и ссылку Circle& в Shape&, но мы сознательно запретили присваивать объекты класса Shape, поэтому вы не имеете права спрашивать, что произойдет, если вы поместите объект класса Circle с определенным радиусом в переменную типа Shape, которая не имеет радиуса (см. раздел 14.2.4). Если бы это произошло, — т.е. если бы мы разрешили такое присваивание, — то возникло бы так называемое “усечение” (“slicing”), похожее на усечение целых чисел (см. раздел 3.9.2).
Итак, попытаемся снова использовать указатели.
vector<Shape*> vps;
vector<Circle*> vpc;
vps = vpc; // ошибка: требуется класс vector<Shape*>
void f(vector<Shape*>&);
f(vpc); // ошибка: требуется класс vector<Shape*>
И вновь система типов сопротивляется. Почему? Рассмотрим, что может делать функция f().
void f(vector<Shape*>& v)
{
v.push_back(new Rectangle(Point(0,0),Point(100,100)));
}
Очевидно, что мы можем записать указатель Rectangle* в объект класса vector<Shape*>. Однако, если бы этот объект класса vector<Shape*> в каком-то месте программы рассматривался как объект класса vector<Circle*>, то мог бы возникнуть неприятный сюрприз. В частности, если бы компилятор пропустил пример, приведенный выше, то что указатель Rectangle* делал в векторе vpc? Наследование — мощный и тонкий механизм, а шаблоны не расширяют его возможности неявно. Существуют способы использования шаблонов для выражения наследования, но эта тема выходит за рамки рассмотрения этой книги. Просто запомните, что выражение “D — это B” не означает: “C<D> — это C<B>” для произвольного шаблонного класса C. Мы должны ценить это обстоятельство как защиту против непреднамеренного нарушения типов. (Обратитесь также к разделу 25.4.4.)
19.3.4. Целые типы как шаблонные параметры
Очевидно, что параметризация классов с помощью типов является полезной. А что можно сказать о параметризации классов с помощью, например, целых чисел или строк? По существу, любой вид аргументов может оказаться полезным, но мы будем рассматривать только типы и целочисленные параметры. Другие виды параметров реже оказываются полезными, и поддержка языком С++ других видов параметров носит более сложный характер и требует обширных и глубоких знаний.
Рассмотрим пример наиболее распространенного использования целочисленного значения в качестве шаблонного аргумента: контейнер, количество элементов которого известно уже на этапе компиляции.
template<class T, int N> struct array {
T elem[N]; // хранит элементы в массиве -
// члене класса, использует конструкторы по умолчанию,
// деструктор и присваивание
T& operator[] (int n); // доступ: возвращает ссылку
const T& operator[] (int n) const;
T* data() { return elem; } // преобразование в тип T*
const T* data() const { return elem; }
int size() const { return N; }
}
Мы можем использовать класс array (см. также раздел 20.7) примерно так:
array<int,256> gb; // 256 целых чисел
array<double,6> ad = { 0.0, 1.1, 2.2, 3.3, 4.4, 5.5 }; // инициализатор!
const int max = 1024;
void some_fct(int n)
{
array<char,max> loc;
array<char,n> oops; // ошибка: значение n компилятору
// неизвестно
// ...
array<char,max> loc2 = loc; // создаем резервную копию
// ...
loc = loc2; // восстанавливаем
// ...
}
Ясно, что класс array очень простой — более простой и менее мощный, чем класс vector, — так почему иногда следует использовать его, а не класс vector? Один из ответов: “эффективность”. Размер объекта класса array известен на этапе компиляции, поэтому компилятор может выделить статическую память (для глобальных объектов, таких как gb) или память в стеке (для локальных объектов, таких как loc), а не свободную память. Проверяя выход за пределы диапазона, мы сравниваем константы (например, размер N). Для большинства программ это повышение эффективности незначительно, но если мы создаем важный компонент системы, например драйвер сети, то даже небольшая разница оказывается существенной. Что еще более важно, некоторые программы просто не могут использовать свободную память. Такие программы обычно работают во встроенных системах и/или в программах, для которых основным критерием является безопасность (подробно об этом речь пойдет в главе 25). В таких программах массив array имеет много преимуществ над классом vector без нарушения основного ограничения (запрета на использование свободной памяти).
Поставим противоположный вопрос: “Почему бы просто не использовать класс vector?”, а не “Почему бы просто не использовать встроенные массивы?” Как было показано в разделе 18.5, массивы могут порождать ошибки: они не знают своего размера, они конвертируют указатели при малейшей возможности и неправильно копируются; в классе array, как и в классе vector, таких проблем нет. Рассмотрим пример.
double* p = ad; // ошибка: нет неявного преобразования
// в указатель
double* q = ad.data(); // OK: явное преобразование
template<class C> void printout(const C& c) // шаблонная функция
{
for (int i = 0; i<c.size(); ++i) cout << c[i] <<'n';
Эту функцию printout() можно вызвать как в классе array, так и в классе vector.
printout(ad); // вызов из