Б. Страуструп - Язык программирования С++. Специальное издание, 3-изд. Бином. 2004 (1160791), страница 142
Текст из файла (страница 142)
д. Так можно понятным образом ввести произвольныс услуги для обычных операций с контейнерами. Однако лучше всего разделять операции, относящиеся к хранению данных, и к их использованию. Последние не относятся к обобшенным распределителям памяти, но их можно предоставить через отдельный аргумент шаблона. 19.4.4. Неинициализированная память Кроме стандартного типа а!!оса!ог, заголовочный файл <тетогу> предоставляет несколько функций для работы с неинициализированной памятью.
Они обладают опасным, но порой очень важным свойством — использовать имя типа Т, чтобы обрашаться не собственно к сконструированному объекту типа Т, а к пространству памяти, достаточному, чтобы хранить объект типа Т. Библиотека обеспечивает три способа для копирования значений в неинициализированную область: !етр1а!е<с1аяя Тл, с1аяя рою > Тагил!л!!!а1!еес! сору ~1лТ!гя1,!л 1ая1, рог гея~ 11 колировалие в гея ( !урес!еТ!урелате дега!ог !гааз<рог та!ие !уре !г; тД!!е ~й я! ~= 1ая!) О колструируея! всея (у" !04.1!) лесе !я!а!!с сая!<ооЫ'> !1,'гея-н.~! 1г ('Тфгя!«<-); ге1игл гея; Глава 19.
Итераторы и распределители памяти 1етр!а1е<с!аяк Гог, с!аяк Т> ооЫ отпЕЕЕа!ЕеееЕ Ге!! (ГогГегк1 Гог1ая1, сопя! Ть*оаЕ) ( ЕурееЕе51урепате нега!ос 1гацк<Гог>гоа!ие 1уре У; О конструируем вГеея1 аЫ!е (Ггя1!=!аяЕ) пещ (к1а11с сая1<ооЫ'> (8*ЯгяЕн-)) У(оаЕ); 1етр1а1е<с!аяя Гог, с1аяк В!ее, с1аяя Т> ооЫ ипЕпЕЕЕа!ЕеесЕ Ге!! и (Гогзегяг, 8!ее л, сопя1 ТЬ оаЕ) ( Еуре<ЕеГ ЕЕЕрепате йега1ог Еганя<Гог>ноа!ие 1уре 1г, ,Е,Е хонтпруируем в7т1 тЫ!е (и — ) пет (ягаЕЕс сая1<ооЫ'> ~Ь'Гегеле-+)) И(оа1); Зги функции предназначены прежде нсене для тех, кз о ресоеизует контейнеры и алгоритмы. Например, используя эти функции Я 19.6(10)), очень легко реализовать гекегие () и гек!ге () Я 163.8).
Будет не очень хорошо, если какой-нибудь неинициализированный объект сбежит из пределов контейнера и попадется обычному пользователю. е См, также 9 ДАА.) Для приемлемого функционирования алгоритмы часто требукзт промежуточного хранилища. Часто такую промежуточную память лучше всего выделять в одной операции, но не иннпиализировать, пока распределение действительно не понадобится. Поэтому для выделения и высвобождения неинициализированной области памяти библиотека обеспечивает пару функций: ,ЕЕ вьшелвет память, не они уиалиэируя: Еетр!а!с<с!акя Т> раЕе Т', рЕгЕЕЕГГ Е> де1 Еетрогагу Ьи0~ег (ри еЕЦЯ ЕЕ висвооождаееп палевпеь, не цничепожия; 1етр!а1е<с!аяк Т> ооЫ ге!игл 1етрогагу Ьи~~ег(T)' Операция де1 Еетрогагу ЬиЯегсХ> (л) пытаегся вылелить область памяти под л яли более объектов типа Х.
Если ей удается выделить какукмто память, она возвращает указатель на первую неинеяциализированную область памяти и число объектов типа Х, способных уместиться в' этой области; в противном случае значение яесопеЕ в паре равно нулю. Идея заключается в том, чтобы система могла хранить число буферов фиксированного размера, готовых к быстрому выделению, так что запрос памяти д..а л объектов может привести к выделению объема памяти большего, чем необходимо для л объектов. Однако может быть выделено и меньше, поэтому способ использования Хе1 Еетрогагу Ьибег () состоит атом, что мы просим побольше, а потом смотрим, сколько окажется в распоряжении. оуфер, полученный функцией де1 1етрогагу ЬиЯег (), после вызова ге!игл 1етрогагу ЬиЯег () должен быть освобожден перед повторным использованием.
Точно так же, как уе1 Еетрогагу ЬиЕЕег () выделяет память под объект без его конструирования, ге1игл 1етрогагу ЬиЯег () высвобождает память без уничтожения объекта. Поскольку де1 1етрогагу ЬиД~ег() является операцией низкого уровня и, вероятно, оепимизированной для работы с временными буферами, ее не следует использовать вместо лещ или а!!осаЕогса!!осаЕе () для выделенп более долговременного хранилища. 641 19.4. Распределители памяти Т рр=йр лещ (ор) Т (оа!); ге!и и *1Ь1к; //поле!<(аетоа1ярр (у" 10ай11) ) гаы кгогауе Нега1огйорега1ог+->() (кз-р; ге1игп*гдгк; ) гам кгогауе 11ега1ог орега1ог>-> (1п1) ( газе к1огауе 11ега1ог Г= '1Ь!к; ге!игл 1; ) Например, мы могли бы нш!нсать шаблон, который копирует содерхсимое контейне- ра вес1ог в буфер: 1етр)а1е<с!акк Т, с!акяА> Т' 1етрогагу <!ир (ое<1ог Т А>й о) ( ра(г<Г, р1гйЩ 1> р-уе1 1етрогагу Ьцфег<Т> (о яеее ()), (Т (о яесопс!< о.я!ее()) ( // прояерка папи ися доалупнои памяти (/(р,Т!гк1!= 0) ге1игл 1етрогагу Ьи//ег(р,р!гяг), ге1игл 0; ) сору (о Ьеугл (), о ел<1 (), гам я1огауе Нега!ос<Т", Т> (р Т!гк1)); гехи гп р~!гк1; Если бы вместо йе1 !етрогагу ЬиЦег () использовался оператор пеки, была бы произведена инициализация.
Когда инициализации избегают, для работы с неинициализированной областью нсобходнм гаш я1огаде 11ега1ог. В данном примере за вызов с(ея1гоу 1етрогагу ЬиОег () для указателя, который получила 1етрагагу с(ир (), отвечает та функпия, которая вызвала 1етрогагу с(ир (). 19.4.5. Динамическое распределение памяти Функции, использованные при реализации операторов лещ и с(е(еге, объявлены в <пеи4> вместе с несколькими связанными с ними средствами: Стандартные алгоритмы, которые записываю~ в последовательность, считают, что элементы последовательности заблаговременно и роинициализнрованы. То есть для записи эти алгоритмы пользу!отея присваиванием, а не копирующим конструктором.
Поэтому мы не можем использовать неинициализированную память как непосредственный выход алгоритма. Это мох<ет оказаться неприятным, поскольку присваивание часто оказывается значительно дорохсе, чем инициализация. Кроме того, нас не интересуют значения, поверх которых мы все равно собираемся произвести запись (иначе мы не стали бы писать повсрх). Решение состоит в использовании гав к1огауе йега1ог из <тетогу>, который производит инициал изагапо, а не присванвание: !етр1а1е<с)аяя Ои1, с)акк Т> с1аяя гаэ ягогауе сгегагог: риЬйс йега1ог<ои1ри1 11ега1ог гау, оо1<1, воин, ооЫ, ооЫ> ( Ои1 р; риЬ!1с.
ехр11с11 гав я1огауе Нега1ог(ои! рр):р (рр)() гап к1огауе 11ега1огй орега1ог" () ( ге1игл "10!к; ) гам я1огауе аега1огй орега1ог= (сопя1Тй оа!) 642 Глава 19. Итераторы и распределители памяти с!аев Ьад овос: риЫтсехсерпол(/'...*/); в!гас! пойуою 1 (); ех1егп сопв1 лай гою 1 погЬгою, // индикатлор выделения палтятпи, // не генерирующего исключений 1урет!ецио!д (*пею Ьапд!ег) (); пеьь Ьапт!!егве1 пею Иалд!ег(пею Ьапд!егпею р) Ягою(), воЫ' орегатогпею (и!ее О Ягою (Ьад а!!ос) ооЫорега1огт!е!е1е(ион!") Ягою (); ио!д* орега1ог пети (Иее 1, сопв1 по1Игою ьт) Ягою (); оо!дорега1огт!е!е1е() (иоЫ*, сопетпойгою И) 1Ьгою(); иоЫ' орега1ог пею(] (вгее О Ягою (Ьад авос), оо!д прего 1ог де!е1е(] (ио!т(') Йгою (); иоЫ' орега1огпею[] (мхе 1, сопв1 поЯгою 1а) Ягою (); иоЫ орега1ог т(е!е1е() (оо!д", сопи по1Игою 11д 1Ьгою (); ио!д" орега1огпею (Иее 1, иоЫ" р) 1Ьгото() ( ге1игпр;) //равлещение (у" !Олб! !) иоЫ орега1ог де1ете() (оо!д* р, ио!д") Ягою () () ооЫ'орега1огпето(] (вые 1, иоЫ*р) 1Ьгою() ( гетигпр;) иоЫ орега1ог де!е1е (иоЫ' р, ио!д') Ягою () ( ) Операторы пею () или пею [] () с незаполненной (пустой) спецификацией исключения (ексерт(оп-зрес(1(сат)оп) (з 14.6) не могут сиптализировать об истощении памяти, генерируя исключение в(т(эбат) а!!ос.
Вместо этого, прн неудачной попытке выделить память они возвращают О. В вьтрагхении-пею (6 6.2.6.2) проверяется значение, возвращаемое аллокаторолт с незаполненной спецификацией исключения; если возвращаемое значение — О, никакой конструктор не вызывается и выраягенив-лею возвращает О. В частности, аллокатор поЯгою возвращает О, а не генерирует исключение в случае безуспешной попытки выделения памяти. оо!д/() ( Я1'р = пею !пт[!00000) ' //люжетл сгенерироватпь Ьад аПос т/ (!пт* д = пею (поЯгото) !пт(!00000)) ( // не сгенерируегп исключения // выделение ~алтяттттт удалось еде( // выделение паяяпщ не удалось ) Это позволяет нам для выделения памяти использовать стратеппо обработки ошибок до генерации исключений.
19.4.6. Выделение памяти в стиле С От С язык С++ унаследовал функциональный интерфейс для динамического распре- де.пения памяти. Его можно найти в <вЫ1Й>: // выделяет и Оатип 643 19.5. Советы иоЫ" та!!ос(з!ее гп) // выделяеп и раз по з байтов, инициализированных нулями иоЫ" са!1ос (згее 1п, зсее 1з); О высвобождает память, выделенную функциял1и тайас() илп сауос() иоЩгее (ио1д'р); // изменяет до з размер л~ассива, на который указывает р; если зпюго не получаепсся, // выделяет з байт и копирует в них массив, на который указывает р иоЫ" геавос (иоЫ" р, зсее ! з); Этих функций следует избегать и предпочитать пеш, с(е!е1е и стандартные контейнеры.
Эти функции работают с неинициализированной памятью. В частности, угее () не вызывает деструкгоров для памяти, которую высвобождает. Реализация пеш и с(е!е!е может пользоваться этими функциями, но нет никакой гарантии, сто она будет это делать. Например, размещать объект при помощи пеш, а упичтожть при помощи й'ее () означает напрашиваться на неприятность. Если вы чувствуете г!отребность воспользоваться геа!!ос (), подумайте, как лучше вместо этого обратиться к стапдар.пюму контейнеру; обычно это проще, и столь же эффективно (9 16З.5). Библиотека также обеспечивает набор функций, предназначенных для эффективного манипулирования байтами. Поскольку С первоначально обращался к байтам без типа через указатели сйагл, эти функции находятся в <сз1г!пд>.
Внутри атих функций с указателями иоЫ* следует обращаются так, как будто это указатели сйагл: // копирование неперекрывающихся областей ио!д* тетсру (иоЫ.'р, сопз1иоЫ'с1, згее гп); // копирование потенциально перекрывающихся областеи иоЫ* тет тоие (иоЫ* р, сопя! иоЫ* у, згее 1 п); Подобно з(геру () Я 20А.1) эти функции копируют и байтов из д в р и возвращают р.
Диапазоны, копируемые функцией теттоие (), могут перекрываться. Однако тетсрд () предполагает, что диапазоны не перекрываются, и обычно оптимизирована с использованием этого допущения: // подобно зггслг!) (у 20г!.1): находит Ь в р[О)..р(п-1) ион!* тетсЬг(сопя! иоЫ'р, сп1Ь, згхе 1п), // подобно зсгстр !): сравнивает последовательноспщ байтов Ы1 тетсгпр (сопл! иоЫ' р, сопя! иоЫ' у, з(хе 1 п); // устанавливает и байтов в Ь, возвращает р ио!д*тетзе1 (ио1д" р, Ы! Ь, з!ее 1п); Многие реализации обеспечивают хорошо оптимизированные версии этнх функций. 19.5.