Бьерн Страуструп. Язык программирования С++. Специальное издание (2011) (1004033), страница 143
Текст из файла (страница 143)
В этих случаях аллокаторы могли бы содержать информацию, типичную для универсальных базовых классов (51б.2.2). Например, можно было бы разработать аллокатор так, чтобы он мог отвечать на вопросы; «где расположены объекты?», «какова их структура?», «содержится ли объект в контейнере?» и т.д. Он мог бы предоставлять сервисы контейнерам, действующим как кэш объектов постоянной памяти, мог бы обеспечивать ассоциативные связи между контейнерами и иными объектами и т.п.
Таким прозрачным способом можно было бы снабдить произвольными услугами обычные контейнерные операции. Однако лучше всего четко разграничивать 19. Итераторы и аллокаторы 682 вопросы хранения данных и вопросы использования данных. Последние не соот- ветствуют обобшснным аллокаторам, и их лучше предоставлять через отдельные параметры шаблона.
19.4.4. Неинициализированная память Помимо стандартного алло като ра (шаблон а!1оса(ог) заголовочный файл <тетогу> определяет несколько функций для работы с неинициализированной памятью. Они обладают опасным, но порой очень важным свойством — использовать имя типа Т, чтобы работать не с корректно сконструированным объектом типа Т, а с областью памяти, достаточной для хранения объекта типа Т. Библиотека предоставляет три способа копировать значения в неинициализированную память: (етр1а1е<с(аяя 1и, с!а(я Гог> Гог ин!н((!а1!(е(( сору (1п )вт, (п (ая1, Гог гея) ( (уре((е! (уренате Мега(ог 1га(за<Го(>:: ча!ие (уре У! вЫ1е (у!гя(! = 1ам) пев (я(аяс сая(<гоЫ*> (а*сея++) ) У(*)(гя(+«); ге1игн гея; ) жсм. (я!0.4.!!) 1етр!а(е<с(аяя Гог, с(аяя Т> юм ин!и!(!а!!яе(( у!!1(Гогу!гя(, Гог (ая(, санге Ть га1) ( (уре((еГ(урепате ((ега1ог (га!«<рог>:: га(ие (уре У( вЫ1е (!1 ге( ! = (аял пев (я(аяс сая(<го!((*> (я "уйя(«+) ) у(гал ) (е(пр(а(е<с1аяя Гог, с!аяя о!яе, с!аяя Т> юЫ ин!ш1!а(!яе(( )(И п (Гог!(гя(, а!яе и, сонм Та оал ( (уре((еГ(урепате вега(ог «ай(<рог>:: га1ие (уре У( в((!1е(п--) пев (я(авс сат<гоЫ*> (а«))гя(+«) ) У(га() ) Эти функции предназначены в первую очередь для разработчиков контейнеров и алгоритмов.
Например, геяегге () и гея(яе () (51б.3.8) легче всего реализуются с помошью этих функций (519.6[10)). Но будет абсолютно неприемлемо, если подобного рода «недоинициализированный объект» выйдет за пределы внутренней реализации контейнера и попадет в руки обычного, конечного пользователя. См. также 5Е.4.4. Часто алгоритмы нуждаются в промежуточной памяти ради приемлемой производительности. И часто такую память лучше всего выделить за одну операцию без соответствуюшей инициализации, оставив таковую вплоть до момента, когда реально в работу потребуется некоторый конкретной кусок такой памяти. В итоге, библиотека предоставляет две функции для выделения и освобождения неинициализированной памяти: ) 9.4, Аллокаторы (распределители памяти) 683 й выделить, но не инициализировать (етр(а(е<с(ат Т> ра(г<Т*,р(г(Щ (> ее( (етрогагу ЬиДег(р(г(Щ О ( 11 освободить, но не уничтоз(сить (етр1а(е<с(ат Т> юЫ ге(игп (етрогагу ЬиЯег(Т*); Функция яе( (етрогагу Ьиууег<Х> (и) пытается выделить память, достаточную для размещения и или более объектов типа Х Если ей удается выделить некоторое количество памяти, то она возвращает указатель на начало выделенной неинициализированной памяти и число объектов типа Х, которые могут там разместиться; в противном случае поле весов(1 в возвращаемой паре равно нулю.
Идея заключается в том, что сама система следит за числом готовых к быстрому выделению блоков памяти заданного размера, так что выделяемый блок может вмещать и более и запрошенных объектов. Но может быть вьшелен и блок размера, в котором помещается меньше, чем и объектов, так что применение функции лет (етрогагу Ьи(уег() состоит в том, что мы просим побольше, а получаем сколько дадут. Память, выделенную функцией яе( (етрогагу ЬиКег(), нужно для ее дальнейшего использования освобождать функцией геп(гп (етрогагу ЬнЯег () .
Аналогично тому, что яе( (етрогагу Ьифег() выделяет память без ее инициализации, функция гоп(гп (етрогагу Ьи3ег() освобождает память без соответствующего уничтожения объектов. Поскольку «е( (етрогагу ЬиЯег() является низкоуровневой функцией и скорее всего оптимизирована для работы с временными буферами, ее не следует использовать вместо операции пе(в или функции а11оса(ог:: а11оса(е () для получения долговременного хранилища. Стандартные алгоритмы, осуществляющие запись в последовательности элементов, рассчитывают на то, что элементы загодя проннициализированы. То есть алгоритмы применяют присваивания, а не копирующие конструкторы. Как следствие, мы не можем непосредственно применить неинициализированную память лля работы алгоритмов.
Все это неприятно, ибо присваивание может оказаться значительно дороже инициализации. Кроме того, нас мало интересуют значения, которые мы будем переписывать (иначе мы бы не стали их переписывать). Решение проблемы заключается в использовании класса га(в могиле Вега(ог из заголовочного файла <тетогу>, который выполняет инициализацию, а не присваивание: (етрйме<с!азз Ои(, с1ат Т> с(азз га(г з(агаве дега(ог: риЫ(с йега(ог<ои(рш пега(ог (ая, юЫ, юЫ, гоЫ, гоЫ> ( Ои( р; риЬЬ'с: ехрдсй га(г з(агаве пега(ог(Ои(рр): р(рр) ( ) ган з(агаве 1(ега(огь орега(ог* () (ге(игп *(Ыз; ) га(г Моголе пега(огь орега(ог= (сопв( Ть га() ( у*рр = в р( пе(г (рр) Т(га(); 11 размен(аел( га1 в рр (з10.в'.11) ге(игп *(Ыз; га(в моголе пега(огв орега(огьь () (ь+р; ге(игп *(Ыз; ) га(в з(агаве йега(ог прего(ос++ ((и() б84 Глава ) 9.
Итераторы и аллокаторы ( гав в(отаве 1(ега(ог( = *(Ыв( вар' ге(иги (; ) )( Напишем, например, шаблонную функцию, которая копирует содержимое контейнера типа гес(ог в буфер: (етр!а(е<с!авв Т, сй(вв А> Т* (етрогагу ((ир (тес(от<Т,А>й г) ( ршг<Т*,р(г(((Я (> р = ее( (стратегу Ьи))ег<Т> (г.ясе () ); Р проверка наличия доступной памяти (Т (р . весен(( < г . в!ге ( ) ) ( (/'(р !(гл( ! = О) ге(иги (етрогату ЬиЯег(р.)(гв() ге(иги О( ) сору(г.Ьел!и (), г.еи(((), гав в(огаее йета(ог<Т*, Т> (р.(ив() ) ге(игп р.!(гв(/ Если бы вместо яе( (етрогагу Ьи()ег ( ) применялась операция иев, то инициализация была бы выполнена.
А поскольку мы избежали инициализации, требуется применять гап яогаяе Ыега(ог для работы с неинициализированной памятью. Наконец, в данном примере клиентский код, вызывающий (етрогагу (!ир (), отвечает за вызов геи(гп (етрогагу Ьи[уег() с указателем, который он [клиент) получает в качестве возврата. 19.4.5.
Динамическая память Средства, применяемые для реализации операций пев и (!е!е(е, объявлены в заголовочном файле <иев>: с(аят Ьа(( а((ос( риЬйс ехсериоп ( /* ... */ ); виис(пайков ( (); ек(еги соил(иойюв (иойюн ( риндикатор выделения памяти, не генерир-го исключений (уре((е! гоЫ (*лев Ьаи(((ег) (); пев Ьап(((ее ля пев Ьап(((ег(пев Ьапй!ег лев р) Йгов ();//вернуть старый пев Ьап(йег юЫ* оретайгиев(в(се П (Ьгов (Ьа(1 а(Ь(с) ( юЫорепиог((е(е(е(юЫ*) Йюн () ( юЫ* орегаип иев(в(ле (, сопл( пойппв и] йюв (); юЫ орепиог ((е(ьче ( юЫь, сопл( ио(Ьпнв (й ) йгон ( ); юЫ* орете(ог лев [) (яте () Йюн (Ь(и( айос) / воЫ орепиог Ие1яе [ ) ( юЫ" ) (Ьюв ( ); юЫ* орет(ог иен [) (ясе (, сопя(ио(люв (й) Йгов (); юЫ орепиог йейпе[) (юЫ", соии пойгов (а) йтов (); ) 9 4.
Аллокаторы (распределители памяти) 685 юЫ* орега(ог пен (в!ге 1, юЫ* р) йгон () ( пчигп р; ) юЫ преп(!огЫе(е(е(юЫ* р, юп!*) Йго(г () ( ) //разме(цение (з!0.4.11! И не делаем ничего юЫ* орега1огпен () (З!ге 1, ю!а* р) йюш () (гетгп р( ) гоЫореюгог((е(е(е(] (гоЫ'р, юЫ*) (Ьго(г () () дне делаем ничего Функции орега(ог пе(в() и орега(ог пе(г(] () с пустыми спецификациями исключений (з14.6) не могут сигнализировать об исчерпании памяти генерацией исключения за1: ( Ьаг! аИос. Вместо этого при неудачной попытке вьщеления памяти они возвращают нуль. В выражении, содержащем операцию пе(г (96.2.6.2), производится проверка возврата от средства выделения памяти с пустой спецификацией исключений; если этот возврат равен нулю, никакого конструктора не вызывается и все выражение также возвращает нуль. В частности, все функции с по(Ьго(г в случае неудачи возвращают нуль и не пытаются генерировать исключение Ьа(! аИос.
Например: го!а'1 ( ) ( (п(* р = пе(г!п1 (100000]; (Г(!и(* о = пе(в(пойгош) т((100000] ) ( // выделение памяти успешно еяе //выделение памяти неуспешно И может сгенерировать Ьа(( аИос И не генерирует исключений Это позволяет применить средства обработки ошибок динамического выделения памяти без генерации исключений. 19.4.6. Выделение памяти в стиле языка С От языка С язык С++ унаследовал функциональный интерфейс для работы с динамической памятью.