2 семестр / Литература / Язык программирования С++. Краткий курс. Страуструп
.pdf70
Глава
3.
Модульность
i-й
элемент
Vector
существует
независимо
от
вызова
оператора
индекса,
по
этому мы можем вернуть ссылку на него.
С другой стороны, по завершении функции ее локальные переменные
чезают, поэтому мы не должны возвращать указатель или ссылку на них:
ис
int& |
bad () |
|
{ |
|
|
|
int |
х; |
|
11 |
". |
|
return |
х;
//
Плохо:
возврат
ссылки
на
локальную
переменную
х
К
счастью,
все
основные
компиляторы
С++
в
состоянии
обнаружить
эту
оче
видную ошибку в bad ( ) . Возврат ссылки или значения
"малого"
типа
эффективно,
но
как
же
пере
давать
из
функции
большие
количества
информации?
Рассмотрим
следую
щий
код:
Matrix operator+(const Matrix& х, |
const Matrix& у) |
|||||
{ |
|
|
|
|
|
|
Matrix |
res; |
|
|
|
|
|
11 ... |
Для |
всех |
res[i,j] |
имеем res[i,j] |
x[i,j]+y[i,j] |
|
return |
res; |
|
|
|
|
|
...
Matrix
11 ...
Matrix
ml, mЗ
=
m2; ml+m2;
//
Без
копирования
Матрица
Matrix
может
быть
очень
большой,
с
дорогостоящим
копирова
нием
даже
на
современном
аппаратном
обеспечении.
Поэтому,
чтобы
избе
жать
копирования,
мы
создаем
перемещающий
конструктор
Matrix
(§5.2.2),
а
операция
перемещения
Matrix
из
оператора
operator+
()
оказывается
очень
дешевой.
Нам
не
требуется
возвращаться
к
ручному
управлению
памя
тью:
Matrix*
add(const
Matrix&
х,
const
Matrix& |
у) |
// Сложный и |
чреватый |
|
11 |
ошибками стиль |
ХХ века |
Matrix* |
р |
= |
new |
11 ... |
Для |
всех |
|
return |
р; |
|
|
Matrix;
*p[i,j],
*p[i,j]
=
x[i,j]+y[i,j]
...
Matrix
11 ...
Matrix*
11 ...
delete
ml,
mЗ
mЗ;
m2; = add(ml,m2);
11 |
Копируем только указатель |
11 |
Легко за быть удалить . .. |
72
Глава
3.
Модульность
Конструкция
auto
[n,
v]
объявляет
две
локальные
переменные n
и
v
с
ти
пами,
выведенными
из
возвращаемого
типа
read
_
entry
().
Этот
механизм
придания
локальных
имен
членам
объекта
класса
называется структурным
связыванием
(structured
Ьinding).
Рассмотрим
еще
один
пример:
map<string,int> |
m; |
|
|
|
||
11 |
... Заполнение |
т ... |
|
|
||
for |
(const |
auto |
[key,value] |
: m) |
||
cout << "1" |
<< |
key |
"," << |
value |
<<
"}\n";
Как
обычно,
можно
декорировать
auto
с
помощью
const
и&.
Например:
void |
incr(map<string,int>&m) |
||
1 |
for |
(auto& |
[key,value] |
|
|||
|
|
++value; |
// m)
Увеличение
значения
кажцого
элемента |
т |
Когда
структурное
связывание
используется
для
класса
без
закрытых
дан
ных,
легко
увидеть,
как
выполняется
такое
связывание:
для
связывания
долж
но
быть
определено
столько
же
имен,
сколько
имеется
нестатических
чле
нов-данных
класса,
и
каждое
введенное
имя
связывается
с
соответствующим
членом.
В
качестве
объектного
кода
по
сравнению
с
явным
использованием
составного
объекта
нет
никакого
отличия;
использование
структурного
свя
зывания - |
просто иное выражение все той же концепции. |
|
Можно также работать и с классами, в которых доступ |
осуществляется |
|
рез функции-члены. Например: |
|
че
complex<douЫe> |
z = |
auto [re,im] = |
z+2; |
{1,2}; // rе=З;
im=2
ит
Объект типа complex имеет два члена-данных, но его интерфейс состо из функций доступа, таких как real () и imag ().Отображение complex
<douЫe>
на
две
локальные
переменные,
такие
как
re
и
im,
осуществимо
и
эффективно,
но
техника,
позволяющая
это
сделать,
выходит
за
рамки дан
ной
книги.
3.7.
Советы
(1]
[2]
Различайте объявления (используемые в качестве интерфейсов) и опре |
||
деления (используемые как |
реализации); §3.1. |
|
Используйте заголовочные |
файлы для представления интерфейсов |
и |
для подчеркивания логической структуры; §3.2; [CG:SF.3). |
|
[3]
[4]
[5]
[6]
[7]
[8] [9]
[1 О]
[11)
[12)
[13)
[14)
[15)
[16)
[17] [ 18)
[19)
[20)
[21]
|
|
|
|
|
|
|
|
|
|
|
|
|
3.7. Советы |
|
73 |
|||
Включайте заголовочные файлы с |
помощью директив |
#include |
в ис |
|||||||||||||||
ходные |
файлы, которые реализуют их функции; §3.2; [CG:SF.5]. |
|
|
|||||||||||||||
Избегайте в заголовочных файлах |
определений функций, не |
являющих |
||||||||||||||||
ся inline; §3.2; [CG:SF.2]. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||
Предпочитайте модули заголовочным файлам (где имеется |
поддержка |
|||||||||||||||||
module); §3.3. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
Используйте |
пространства |
имен для выражения |
логической |
структуры; |
||||||||||||||
§3.4; [CG:SF.20). |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||
Используйте |
директивы using |
при переделке |
приложений, для базовых |
|||||||||||||||
библиотек (таких, как std) или в |
локальной |
области |
видимости; |
§3.4; |
||||||||||||||
[CG:SF.6] [CG:SF.7]. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||
Не помещайте |
директивы using в |
заголовочный файл; |
§3.4; [CG:SF.7]. |
|||||||||||||||
Генерируйте |
исключения для указания того, что |
вы не |
в состоянии вы |
|||||||||||||||
полнить определенную задачу; |
§3.5; [CG:E.2]. |
|
|
|
|
|
|
|
|
|||||||||
Используйте |
|
исключения |
только |
для |
обработки |
ошибок; |
§3.5.3; |
|||||||||||
[CG:E.3]. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||
Применяйте |
коды ошибок |
там, где |
предполагается, что |
обрабатывать |
||||||||||||||
ошибки будет |
непосредственно |
вызывающая функция; |
§3.5.3. |
|
|
|||||||||||||
Генерируйте |
исключения, если |
предполагается, что ошибка |
должна пе |
|||||||||||||||
редаваться через несколько |
вызовов |
функций; |
§3.5.3. |
|
|
|
|
|
||||||||||
Если |
вы не знаете, что лучше использовать, исключения или коды |
оши |
||||||||||||||||
бок, |
предпочитайте исключения; §3.5.3. |
|
|
|
|
|
|
|
|
|
||||||||
Разрабатывайте стратегию обработки ошибок как можно раньше при |
||||||||||||||||||
проектировании; §3.5; [CG:E.12). |
|
|
|
|
|
|
|
|
|
|
|
|
||||||
Используйте |
для исключений специально |
разработанные |
пользователь |
|||||||||||||||
|
|
|
|
|||||||||||||||
ские |
типы (не |
встроенные типы); |
§3.5.1. |
|
|
|
|
|
|
|
|
|
||||||
Не пытайтесь |
перехватывать все |
исключения в |
каждой функции; |
§3.5; |
||||||||||||||
[CG:E.7]. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||
Предпочитайте идиому RAll явным |
trу-блокам; |
§3.5.1, §3.5.2; [CG:E.6). |
||||||||||||||||
Если |
ваша функция не может |
генерировать |
исключения, объявите ее |
|||||||||||||||
как noexcept; §3.5; [CG:E.12). |
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||
Конструктор |
должен обеспечивать выполнение инварианта |
и генериро |
||||||||||||||||
вать |
исключение, если не в |
состоянии это |
сделать; |
§3.5.2; [CG:E.5]. |
||||||||||||||
Проектируйте |
свою стратегию обработки |
ошибок |
вокруг |
инвариантов; |
||||||||||||||
§3.5.2; [CG:E.4]. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||
То, что |
можно проверить во время |
компиляции, обычно |
лучше |
прове |
||||||||||||||
рять |
во |
время |
компиляции; |
§3.5.5 [CG:P.4] [CG:P.5]. |
|
|
|
|
|
74
Глава
3.
Модульность
(22]
(23]
(24]
(25] (26]
Передавайте "малые" значения
по ссылке; §3.6.1; [CG:F.16].
в
функции
по
значению,
а
"большие"
-
Предпочитайте |
передачу по константной |
неконстантной |
ссылке; (CG:F.17]. |
ссылке
передаче
по
обычной,
Возвращайте |
информацию из |
|
ей значений (а не с |
помощью |
|
[CG:F.21]. |
|
|
функций выходных
в
виде возвращаемых функци
параметров); §3.6.2; [CG:F.20]
Не |
злоупотребляйте |
выводом типа возвращаемого значения; §3.6.2. |
Не |
злоупотребляйте |
структурным связыванием; именованные возвра |
щаемые типы зачастую являются более ясной документацией; §3.6.3. |
4 Классы
|
- |
+ |
Введение |
+ Конкретные типы |
|
|
Арифметический тип |
|
Контейнер |
+ |
Инициализация контейнеров |
Абстрактные типы |
|
+ |
Виртуальные функции |
+ |
Иерархии классов |
|
Преимущества иерархий |
|
Навигация по иерархии |
|
Избежание утечки ресурсов |
•Советы
Эти типы не абстрактны - |
они реальны |
так же, как и типы int и douЫe. |
|
|
Дуг МакИлрой |
4.1.
Введение
Цель
этой
и
следующих
трех
глав
-
дать
вам
представление
о
поддержке
абстракции
и
управления
ресурсами
С++
без
погружения
в
детали.
•
•
В этой главе неформально представлены способы определения и ис |
||||
пользования новых типов (пользовательских типов). В частности, здесь |
||||
представлены основные |
свойства, методы реализации и языковые сред |
|||
ства, используемые для |
конкретных классов, абстрактных классов и |
|||
иерархий |
классов. |
|
|
|
В главе 5, |
"Основные операции", представлены операции, |
которые име |
||
ют определенный смысл в С++, такие как конструкторы, |
деструкторы |
|||
и присваивания. В ней излагаются правила их совместного использова |
||||
ния для управления жизненным циклом |
объектов и поддержки просто |
|||
го, эффективного и полного управления |
ресурсами. |
|
76
Глава
4.
Классы
•
•
В главе 6, "Шаблоны'', представлены шаблоны как механизм для пара |
|||
метризации типов и алгоритмов (другими) типами и алгоритмами. Вы |
|||
числения над пользовательскими и встроенными типами представлены |
|||
в виде функций, иногда обобщенных |
до шаблонных функций и функцио |
||
ншzьных объектов. |
|
|
|
В главе 7, "Концепты и обобщенное |
программирование", представлен |
||
обзор концепций, технологий и языковых возможностей, лежащих в ос |
|||
нове обобщенного программирования. Основное внимание |
уделяется |
||
определению и использованию концептов для точных определений ин |
|||
терфейсов шаблонов и проектирования. Вводятся шаблоны |
с перемен |
||
ныJи |
количеством параметров (вариативные шаблоны) для |
определе |
|
ния |
наиболее общих и наиболее гибких интерфейсов. |
|
Это |
языковые средства, поддерживающие |
стили программирования, извест |
ные |
как объектно-ориентированное программирование и обобщенное про |
|
граммирование. В главах 8-15 приводятся |
примеры возможностей стандарт |
|
ной |
библиотеки и их использования. |
|
Центральной
особенностью
языка
С++
является
класс.
Класс
-
это
поль
зовательский
тип,
представляющий
в
коде
программы
некоторую
концепцию.
Всякий
раз,
когда
наш
дизайн
программы
содержит
полезную
концепцию,
идею,
сущность
и
тому
подобное,
мы
стараемся
представить
ее
в
программе
в
виде
класса,
чтобы
идея
содержалась
в
коде,
а
не
только
в
наших
головах,
проектной
документации
или
в
некоторых
комментариях.
Программу,
по
строенную
из
хорошо
подобранного
набора
классов,
намного
легче
понять
и
сделать
безошибочной,
чем
программу,
которая
строится
непосредственно
на
фундаменте
фундаментальных
встроенных
типов.
В
частности,
библиотеки
часто
предлагают
именно
наборы
классов.
По
сути,
все
языковые
средства
(помимо
основных
типов,
операторов
и
инструкций)
существуют
для
того,
чтобы
помочь
определить
лучшие
классы
или
более
удобно
их
использовать.
Под
"лучшими"
классами
я
имею
в
виду
более
правильные,
более легкие
в
обслуживании,
более эффективные, более
элегантные,
более
простые
в
применении,
более удобочитаемые
и
надежные.
Большинство
методов
программирования
опирается
на
разработку
и
реализа
цию
конкретных
видов
классов.
Потребности
и
вкусы
программистов
очень
разнообразны, а смотрим только
потому базовую
и поддержка классов очень обширна. Здесь мы рас поддержку трех важных разновидностей классов:
• • •
конкретные классы (§4.2);
абстрактные классы (§4.3);
классы в иерархиях классов (§4.5).
4.2.
Конкретные
типы
77
Поразительное
количество
полезных
классов
оказываются
принадлежащими
одной
из
этих
трех
разновидностей.
Еще
больше
классов
можно
рассматри
вать как простые варианты этих разновидностей или зованием сочетаний используемых для них методов.
реализуемые
с |
исполь |
4.2.
Конкретные
типы
Основная
идея
конкретных
классов
заключается
в
том,
что
они
ведут
себя,
"как
встроенные
типы".
Например,
тип
комплексного
числа
или
целое
число
с
бесконечной
точностью
очень
похожи
на
встроенный
int,
за
исключением
того,
что
они
обладают
собственной
семантикой
и
собственными
наборами
операций.
Аналогично
vector
и
string
очень
похожи
на
встроенные
масси
вы,
но
ведут
себя
куда
лучше
(§9.2,
§10.3,
§
11
.2).
Определяющей характеристикой конкретного представление является частью его определения.
типа является то, Во многих важных
что его случаях,
таких
как
vector,
это
представление
является
лишь
одним
или
нескольки
ми
указателями
на
данные,
хранящиеся
в
другом
месте,
но
это
представление
присутствует
в
каждом
объекте
конкретного
класса.
Это
позволяет
обеспе
чить
максимальную
эффективность
во
времени
и
пространстве.
В
частности,
это
позволяет:
•
•
•
•
помещать объекты конкретных типов в стек, |
в статически выделенную |
|||
память и в другие объекты(§1.5); |
|
|
|
|
обращаться к объектам непосредственно |
(а не только с помощью указа |
|||
телей или ссылок); |
|
|
|
|
непосредственно и полностью инициализировать |
объекты |
(например, |
||
с использованием конструкторов; §2.3); |
|
|
|
|
копировать и перемещать объекты (§5.2). |
|
|
|
|
Представление
может
быть
закрытым
(как
для
Vector,
§ 2.3)
и
доступным
только
через
функции-члены,
но
оно
присутствует.
Поэтому,
если
представ
ление
изменяется
каким-либо
существенным
образом,
пользователь
должен
перекомпилировать
исходные
тексты. Это
цена
того,
что
конкретные
типы
ве
дут
себя
точно
так
же,
как
встроенные
типы.
Для
типов,
которые
меняются
нечасто
и
в
которых
локальные
переменные
обеспечивают
столь
необходи
мую
ясность
и
эффективность,
это
вполне
приемлемо
и
часто
просто
идеаль
но.
Чтобы
повысить
гибкость,
конкретный
тип
может
хранить
основные
ча
сти
своего
представления
в
свободной
памяти
(динамической
памяти,
куче)
и
получать
доступ
к
ним
через
часть,
хранящуюся
в
самом
объекте
класса.
Так
реализованы |
vector и string; их можно |
с тщательно |
отработанными интерфейсами. |
считать
дескрипторами
ресурсов