1 ПРОБЛЕМЫ ПРИ ЯВНОМ РАСПРЕДЕЛЕНИИ ПАМЯТИ В С++, СПОСОБЫ ИХ РЕШЕНИЯ. ССЫЛКИ И УКАЗАТЕЛИ. РАСПРЕДЕЛЕНИЕ ПАМЯТИ ПОД ПЕРЕМЕННЫЕ, УПРАВЛЕНИЕ ПАМЯТЬЮ С ПОМОЩЬЮ ПЕРЕОПРЕДЕЛЕНИЯ ОПЕРАТОРОВ NEW И DELETE. Работа с функциями
2 К нововведениям C++- относятся встроенные функции (inline functions), передача переменных по ссылке, используемые по умолчанию значения аргументов, перегрузка функций (полиморфизм) и шаблонные функции. Все перечисленные функциональные возможности нам и предстоит рассмотреть в данной главе. В отличие от всех предыдущих глав, здесь больше внимания уделяется изучению программных средств, которые присущи именно языку С+-+, но не С. Таким образом, с этого момента начинается наше главное вторжение в мир "плюсов". Работа с функциями
3 Встроенные (inline) функции это новое инструментальное средство С+-+, предназначенное для повышения быстродействия программ. Основное различие между обычными и встроенными функциями заключается не в программном коде, а в том, как компилятор встраивает их в программу. Конечный продукт процесса компиляции это исполняемая программа, которая состоит из набора команд на машинном языке. Когда мы запускаем программу, операционная система загружает эти команды в оперативную память компьютера. Поэтому каждая команда имеет свой собственный адрес в памяти. Затем компьютер последовательно выполняет эти команды. Иногда в программе встречается цикл или условный оператор, тогда программа пропускает часть команд, осуществляя переход вперед или назад к определенному адресу. Вызов обычной функции также влечет за собой переход выполняемой программы к другому адресу (адресу функции) и возврат в прежнее место, когда выполнение функции будет завершено. Встроенные функции
4 Рассмотрим подробнее, как обычно протекает этот процесс. Когда программа переходит к команде, вызывающей функцию, она запоминает адрес команды, которая непосредственно следует за командой вызова функции. Затем программа копирует аргументы функции в стек, переходит к ячейке памяти, которая представляет собой начало функции, выполняет код функции. Затем программа возвращается к выполнению команды, адрес которой она запомнила. Такое перепрыгивание с места на место в коде и запоминание соответствующих адресов при использовании функций приводит к дополнительным затратам времени. Встроенные функции
5 Описанному выше процессу существует альтернатива встроенные функции C++. Скомпилированный код такой функции встраивается в код программы - компилятор подставляет вместо вызова функции ее код. В результате программе не нужно выполнять переход и возвращаться назад. Таким образом, встраиваемые функции выполняются немного быстрее, чем обычные, однако при этом требуется дополнительный расход памяти. Если в 10 различных местах программа выполняет 10 отдельных вызовов одной и той же функции, то компилятор создает 10 копий этой функции (рис. 8.1). Встроенные функции
6
7 Решение об использовании встроенной функции требуется принимать с учетом обстоятельств. Если затраты времени на выполнение функции значительно превышают длительность реализации механизма ее вызова, экономия времени на фоне общего процесса незаметна. Если время выполнения кода невелико, то разница во времени при использовании встроенной функции по сравнению с обычной может быть значительной. С другой стороны, в этом случае ускоряется сравнительно быстрый процесс, поэтому общая экономия времени может быть невелика, если функция вызывается нечасто. Встроенные функции
8 Чтобы воспользоваться встроенной функцией, нужно выполнить следующие действия: Предварить объявление функции ключевым словом inline. Предварить определение функции ключевым словом inline. Общепринятой практикой является опускать прототип и помещать полное описание функции там, где обычно находится прототип. Компилятор не обязательно удовлетворит запрос пользователя сделать функцию встроенной. Он может прийти к заключению, что функция слишком велика, или обнаружит, что она обращается сама к себе (рекурсия для встраиваемых функций не допускается и невозможна в принципе). Кроме того, компилятор может не поддерживать эту опцию. Встроенные функции
9
10 Встроенные функции
11 В языке C++ вводится новый составной тип данных ссылочная переменная. Ссыпка представляет собой имя, которое является альтернативным или псевдонимом для ранее объявленной переменной. Однако основное назначение ссылок применение их в качестве формальных аргументов функций. Используя ссылку в качестве аргумента, функция работает с исходными данными, а не с их копиями. Ссылки представляют собой удобную альтернативу указателям при обработке крупных структур посредством функций, они широко используются при разработке классов. Ссылочные переменные
12 Рассмотрим основы определения и применения ссылок. Целью предстоящего обсуждения является не демонстрация функционирования ссылок, а типичные методы их использования. Ссылочные переменные
13 В языках С и C++ символ & используется для обозначения адреса переменной. Однако в C++ символу & придается дополнительный смысл: он также за- действуется для объявления ссылок. Например, чтобы rodents стало альтернативным именем для переменной rats, необходимы следующие операторы: int rats; int & rodents = rats; // rodents становится псевдонимом имени rats В этом контексте символ & не является операцией адресации: он воспринимается как часть идентификатора типа данных. Подобно тому, как выражение char * в объявлении означает указатель на значение типа char, выражение int & является ссылкой на тип int. Объявление ссылки позволяет взаимозаменяемо использовать идентификаторы rats и rodents для ссылки на одно и то же значение и одну и ту же ячейку памяти. Создание ссылочных переменных
14 Создание ссылочных переменных
15 Создание ссылочных переменных
16 Создание ссылочных переменных
17 Создание ссылочных переменных
18 Создание ссылочных переменных
19 Создание ссылочных переменных
20 Создание ссылочных переменных
21 Чаще всего ссылки используются в качестве параметров функции, при этом имя переменной в функции становится псевдонимом переменной из вызывающей программы. Такой метод передачи аргументов называется передачей по ссылке. Передача параметров по ссылке позволяет вызываемой функции получить доступ к переменным в вызывающей функции. Передача по значению, как известно, приводит к тому, что вызываемая функция работает с копиями значений из вызывающей программы (рис. 8.2). Ссылки в роли параметров функции
22 Ссылки в роли параметров функции
23 Теперь сравним использование ссылок и указателей при решении простой компьютерной задачи организации обмена значениями двух переменных. Функция обмена должна иметь возможность изменять значения переменных в вызывающей программе. Поэтому обычный подход, т.е. передача переменных по значению, здесь неприемлем, поскольку функция выполнит обмен содержимым лишь для копий исходных переменных. А вот при передаче ссылок функция получит возможность работать с исходными данными. Для получения доступа к исходным данным можно также передавать указатели. В листинге 8.4 демонстрируются все три метода, включая и тот, который не дает желаемого результата, так что вы можете сравнить их. Ссылки в роли параметров функции
24 Ссылки в роли параметров функции
25 Ссылки в роли параметров функции
26 Ссылки в роли параметров функции
27 Вывод программы имеет следующий вид Ссылки в роли параметров функции
28 Передача аргументов по ссылке (swapr (walletl, wailet2)) и передача по значению (swapv(walletl, wallet2)) выглядят идентичными. Определить, что функция swapr () передает аргументы по ссылке, можно только обратившись к прототипу или к описанию функции. В то же время наличие операции адресации (&) явно указывает, что функции передается адрес значения (swapp(&walietl, &wallet2)) напомним, что объявление типа int *p означает, что р указатель значения типа int, и поэтому аргумент, соответствующий р, должен быть адресом, таким как, например, swalletl. Ссылки в роли параметров функции
29 Далее сравним программные коды функций swapr () (передача по ссылке) и swapv() (передача по значению). Единственное видимое различие между ними состоит в объявлении параметров: void swapr(int & a, int & b) void swapv(int a, int b) Внутреннее различие между ними, естественно, заключается в том, что в функции swapr() переменные а и Ь служат в качестве псевдонимов имен walletl и wallet2, так что обмен значениями между а и b вызывает обмен значениями между переменными walletl и wallet2. В то же время в функции swapv () переменные а и b это новые переменные, которые содержат копии значений переменных walletl и wallet2. В этом случае обмен значениями между а и b никак не влияет на переменные walletl и wallet2. Ссылки в роли параметров функции
30 И, наконец, сравним функцию swapr () (передача по ссылке) и swap () (передача указателей). Первое различие заключается в объявлении параметров: void swapr(int & a, int & b) void swapp(int * p, int * q) Второе различие состоит в том, что вариант с указателем требует применения операции разыменования (*) во всех случаях, когда в функции используются переменные р и q. Как уже говорилось, ссылочную переменную необходимо инициализировать при ее определении. Можно считать, что аргументы-ссылки инициализируются с присвоением значений, переданных при вызове функции. Таким образом, вызов функции swapr(walletl, wallet2); инициализирует формальный параметр а с присвоением значения wallet1 и формальный параметр b с присвоением значения waiiet2 Ссылки в роли параметров функции
31 Использование ссылочных аргументов имеет ряд особенностей. Сначала обратимся к листингу 8.5. В представленной здесь программе используются две функции для возведения значения аргумента в куб. Одна из них принимает аргумент типа double, в то время как другая получает ссылку на значение типа double. Свойства и особенности ссылок
32 Свойства и особенности ссылок
33 Получаем следующий результат: 27 = cube of 3 27 = cube of 27 Обратите внимание на то, что функция refcube () изменяет значение х в функции main (), a cube () нет. Именно поэтому, как правило, используется передача по значению. Переменная а является локальной для функции cube (). Она инициализируется значением х, однако изменения, которым подвергается а, не отражаются на х. Тем не менее, поскольку функция refcube () использует в качестве аргумента ссылку, те изменения, которым она подвергает переменную ra, фактически отражаются и на х. Свойства и особенности ссылок
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125