Б. Страуструп - Язык программирования С++. Специальное издание, 3-изд. Бином. 2004 (1160791), страница 38
Текст из файла (страница 38)
Обзор операторов Операторы инкремента и декремента особенно полезны при модификации переменных в циклах. Например, копирование строки, ограниченной нулем, можно записать следующим образом: оогд сру (спас* р, сопв1 сваг' у) ( шд!1е ("рьь = *у-н-); Также как н С, С+и- и любят и ненавидят за возможность написания подобного, ори- ентированного на выражения, кода. Так как выражение шй11е ('рч и = 'у-н-); выглядит более чем странно для программистов, пишущих не на С, а такой код довольно часто встречается в С и С++, стоит рассмотреть этот фрагмент более подробно. Сначала рассмотрим более привычный способ копирования массивов символов: !п11епай = згг(еп (д); уаг (1п! 1=0; !к=1епугй; 1+а) р[!) = 0[1); Это довольно расточительно. Длина строки, ограниченной нулем, определяется путем просмотра этой строки н поиском завершающего нуля.
Поэтому мы просматриваем строку дважды: первый раз при определении размера, и второй — прп копировании. Перепишем пример следующим образом: (п11; Уог (1=0; 0[1) шО; 1<-ь) р[1) = у[1); р[ч = О; 0 завершающий ноль Можно убрать переменную 1, используемую в качестве индекса, так как р и о являют- ся указателями: /1 указывает на следующий сиявол //указывает на следующий сил~вал р=О, // завершаюи(ии коль Постинкремептная операция сначала использует значение, а потом его увеличивает, потому мы можем переписать цикл следующим образом: шИ1е ("у!= 0) ( рьь = у+э; *р = О, 1/ завершающий ноль Значением выражения *р++ = 'о++ является *о. Поэтому, мы можем переписать при- мер так: ш(и(е (~;ркч =*ум)!=0) () В этом примере, равенство 'д нулю обнаружнваегся только после того, как символ скопирован в "р и выполнен инкремеит р. Таю1м образом мы избавились от строки, в которой производилось присваивание завершающего нуля.
И, наконец, мы можем сокра- 168 Глава 6. Выражения и инструкции тить код, убрав пустой блок операторов. Кроме того, сравнение «! = 0» является лишним, так как указатель и так сравнивается с нулем. Таким образом, мы получаем выражение, с которого и начали: савве )'р-н- = 'д-н-), Является ли эта версия менее читабельной? Для опытного программиста на С пли С++ -- нет. Является ли эта версия более эффективной с точки зрения времени выполнения или размера кода, чем предыдущая? За исключением варианта с вызовом эЫеп )), далеко не обязательно.
Какая версия эффективней, зависит от архитектуры машины и компилятора. Самым эффективным способом копирования строки, ограниченной нулем, на вашеи конкретной машине должно быть использование стандартной функции копирования строк; сйаг*етгсру )сваг', салаг сдпг'); О иахс1ится в <хгсиуЯ> Можно воспользоваться более обобщенным алгоритмом копирования сару (э 2.7.2, 9 18.6,1), Там, где это возможно, пользуйтссь средствами стандартной библиотеки для работы с указателями и байтами.
Стандартные библиотечные функции могут быть встроены в место вызова (9 7.1.1) либо реализованы с использованием специализированных машинных инструкций. Поэтому проведите тщательные измерения, для того чтобы убедиться в том, что некоторый фрагмент вручную написанного кода работает быстрсе библиотечной функции. 6.2.6. Свободная память Время жизни плгенованного объекта определяется его областью видимости (ч 4.9.4). С другой стороны, часто возникает необходимость в создании объектов, которые сушествуют вне зависимости от области видимости, в которой они были созданы. Типичным примером является создание объектов, которые используются после возвращения из функции, в которой они были созданы. Такие обьекты создаются при помощи оператора пеш и уничтожаются при помощи оператора с(е1е1е.
Говорят, что место под объекты, созданные при помощи оператора пеш, выделяется из «свободной памяти > (такие объекты называют сшс «объектами из кучи> илц «объектами, размещаемыми в динамической памятия). Давайте посмотрим, как мы могли бы написать компилятор в стиле настольного калькулятора (9 6.1). Функции синтаксического анализа могли бы следующим образом строить дерево выражений для его последующего использования генератором кода: и!гисг Епас)е ( Тепел иа!ие арег, Еловое" 1е~ф, Еловое* г)уйг.
Епас1е* ехрг )Ьаа! деГ) Епайе* 1еД= 1егт )уег), ра~ (ь) 169 б.2. Обзор операторов кт!1с)ь (сиге 1оп) ( саке РШЯ. саяе МАЛ!5: Еподе' и = лего Еподе; л->орег = сигг 1о)ь; л->!е11 = 1е/7, л — > гсуЫ= 1егт (1гие); 1езг= л; ' Ьгеай; //создогпь Елоде в свободной лслятн де1аи11: ге1игп 1е/Г, 0 вернуть узел Генератор кода может использовать зтп узлы и затем нх удачнтгс оо!Й депега1е (Еподе' л) ( кт!1св (л — >ореь) ( саяе РШБ //-.
де!е1е л; //удалить Елодс из свободной лал~ятп 6.2.6.1. Массивы Прп помощи оператора пещ можно создавать гиассивы объектов. Например: с!ьаг* яаое ягг!лу (сопя! с!ьаг' р) ( с!ьаг*к = пет с!гаг(ягг!ел (р)~-!]; я1 ору(к,р), /,! скопировать р в к ге1игп я; ) !п1ьпа!и (!п1 агус, с(ьаг' агро()) ! ь/ (агус < 2) ех!1 (!); Объект, созданный при помощи оператора лев, существует до тех пор, пока он не улален прн помощи оператора с(е1е1е. После это~о памятгь занимаемая объектом, может быть снова использована следующим оператором пев. Реализация Сн-ь не гарантирует наличие «сборщика мусораь, осуществля!ощего поиск объектов, на которые отсутствуют ссылки, и делающего эту память доступной для повторного использования оператором лев.
Соответственно, я полагаю, что объекты, созданные при помощи оператора пещ, удаляются вручную при помшци оператора с(е1еге. Прп наличии сборщика мусора в большинстве случаев можно было бы обойтись без использования оператора с(е(е1е 19 В.9.1). Оператор а1е1е1е можно применять либо к указателю, возвращенному оператором пеиз, либо к нулю.
Применение с(е1е1е к нулю не вызывает никаких действий. Можно определить и более специализированную версию оператора лев (9 15.6). Глава б. Выражения и инструкции 170 е?ъаг* р = ваие я1гггъа '(агни(1]), // йе!е1е)] р, Оператор с(е1еге используется для удаления отдельных объектов, с(е1е1еЦ -- для удаления массивов. Для того чтобы освободить память, выделенную оператором пеш, операторы с?е1е1е и с(е1е1еЦ должны иметь возможность определить размер объекта. Из этого следует, что стандартная реализация пеш выделяет памяти немного больше, чем потребовалось бы для статического обьекта, Как правило, используется одно дополнительное слово для хранения размера объекта. Обратите внимание, что оес1ог Я 3.7.1, () 16.3) является корректным объектом, и поэтому память под него можно выделить н освободить при помощи простых пеги и с1е1е1е.
Например; иоЫ/(иъг и) //объект //массив объвкпюв иве1иг<т1 ' р = пет иес1ог<1п1 (и), ипп у = пеш т1[п], 0- де(е1е р, де(е1е() д, Оператор с1е1е1еЦ может применяться только к указигслю на массив, полученному посредством пеки, пли к нулю. Применение с(е1е1еЦ к нулю пе дает какого-либо эффекта. 1гу ( /ог (ь) лет сдпг(! 0000], 6.2.б.2. Отсутствие памяти Операторы выделения и освобождения памяти пеш, с(е1е1е, пеиЦ и сге1е1е[] реализованы прп помогдп функций, представленных в заголовочном файле <пев> Я 19А.5): иоЫ* врега1ог пеиг (ягее 1); //выделяет намять длл индивидуального объекта иоЫ орега1огде1вге (иогд'р); //если р?=0, то огвобождиеяг память, //выделенную пеид) иоЫ* врега1огпеш(](ягяе 1), //выделяет память для миссива иоЫ орегагогде1вгв(](ио!И'р); //еми р1=0, то освобождает аалгять, //выделенную ае4)(] Для выделения памяти под объект пеш вьюываег орегагог пеиг(), выделяющий подходящее количества байт.
Аналогично, для выделения лама~и под массив пенг вызывает орега1ог ленгЦ(). Стандартная реализация орегаг агаев () и орега1ог пешЦ() не инициализирует выделяемую память. Что происк ъдит, когда пеш не может найти достаточного объема памяти? По умолчаншо генерируется исключение Ьасг аИос. Например: иоЫ/(] ( 171 б.2. Обзор операторов сагой (Ьад а!!ос) ( сегг « '11е~л свободной памятисчп', ) Когда вся память будет использована, вызовется обработчик исключения Ьас( а!!ос.
Мы можем вручную указать, какие действия должны выполниться, когда оператор пеш не может выделить память. Прн неудачной попытке выделения памяти пеш сначала вызывает функцию, указанную аргументом при вызове зе1 песо Ьапс)!ег (), объявленной в «пеш» !если такой вызов вообще имел место).