Бьерн Страуструп. Язык программирования С++. Специальное издание (2011) (1004033), страница 167
Текст из файла (страница 167)
я. я(аг< ( ) < 22.4.6. Массив 81[се аггау Из массива га1апау и среза мы можем построить нечто похожее на га1аггау, но на самом деле являющееся лишь способом обратиться к подмножеству массива, адресуемого срезом. Это я1!се аггау, который определяется следующим образом: (етр1а(е<с!аяя Т> с!аяя яЫ:: я(<се аггау ( риЫс: <уре<(е1' Т го<ив 1уре < Поскольку срез имеет размер, мы даже можем обеспечить проверку корректности диапазона элементов. Я воспользовался я11се::я[ее(), чтобы ввести операцию еп<1(), предоставляющую итератор, настроенный на элемент, следующий за последним элементом среза.
Поскольку срез может описывать либо строку, либо столбец, Я<се 1(ег позволяет проходить га1аггау как по строкам, так и по столбцам. Сравнения можно определить следующим образом: Т90 го1й орега!ог= (сопке ча!аггау<Т>ь) ! го!й орегагог= (сопл! Та га1) г // присвоить ча! каждая~у элеиенту ко!й орега!ог* = ( сопл! га(аггау< Т>ь га! ); // чя в=оа!ц для каждого элемента //аналогично: /=, %~'=, +=, -=, ь=, &=, )=, <<=, >>= -з1!се аггау () рг(ча!е: зйсе аггау (); // предотвращаем конструирование зйсе аггау (солт зйсе аггауь) ! У предотвращаем копирование зйсе аггауз орегагог= (сопл! зйсе аггауь); //предотвращаем копирование //эависящее от реализации представление Пользователь не может напрямую создавать объекты типа зйсе азтау.
Вместо этого пользователь индексирует за1агтау, чтобы создать зйсе агтау для данного среза. Как только зйсе агтау инициализирован, все обрашения к нему косвенно передаются га1аггау, для которого он создан. Например, мы можем создать нечто, что представляет каждый второй элемент массива следуюшим образом: го1й (( ва1аггау<йоиЫе> ь й) ( зйсе аггау<йоиЫе>ь г егеп = й(зйсе(О,й.з1зе() /2ьй.з(зе() ьь2,2) ) зисе аггау<йоиЫе>а г ойй = й(зйсе(1,й.з/ге() /2,2) ); г егеп *= г ойй! г ойй = О; ) Запрет на копирование зйсе азтау необходим, чтобы поддержать оптимизации, рассчитывающие на отсутствие синонимов объектов.
Это может оказаться стеснительным. Например: зИсе аггау<йоиЫе> гов(га!аггау<йоиые>ь й, тг1) ( зйсе аггау<йоиЫе> г = й(зйсе (О, 2, й.з!зе () /2) ]; /еггог: попытка копирования гепггп й(з1!се (!гь2, г', й. з/ге ( ) /2) ); // еггог: попытка копирования ) Часто копирование срезов является разумной альтернативой копирования зйсе аггау. Срезы могут использоваться для формирования разных подмножеств массивов. Например, можно применить срезы для манипулирования непрерывно расположенными подмассивами следующим образом: (пйпе з1!се зио аггау (з1зе (1тзг, з/зе ! соил!) ( гесагп зйсе ~Ягзз, соил(, 1); //(() гз!у)гзгьсоип(( го!й Г (ча!аггау<йоиЫе>а з) ча1аггау<Т>* р; зйсе ю ); Глава 22. Классы для математических вычислений У перемножить элементы попарно и сохранить //результат в четных элементах //присвоить О каждому нечетному элементу й 22.4.
Векторная арифметика 79'< га(а((ау<((оиЫе> Ьа117 (п) ( га(аггау<доиВ1е> Во<72 (п2) ( // копирование первой половины г /У копирование второй половины » Стандартная библиотека не предоставляет никаких матричных классов. Вместо этого, она предлагает использовать га1аггау и срезы в качестве инструмента построения матриц, оптимизированных для разных задач. Рассмотрим, как можно при помощи га1аггау и з11се азгпу реализовать простую двумерную матрицу: з(ге (яге () сопи! (ге<игп <(1*(<2( ) яге < аут1 () сопз( (ге(игп <(1( ) Укол-во столбцов яге ( аут2 () сопя( (ге(игп ((2( ) //кол-во строк Яке 1<ее«(оиЫе> го(в(в<ге (1); Сздсе 1<ее<((оиЫе> го(в (з(ге «) сопя(; б(ке «ег<((оиВ(е> со!итп (яге < (); Сз!ке !<ег<((оиЫе> со1итп (яге ( () сопя(; ((оиЫез орега<ог() (яге (х, з(ге <у); ((оиЫе орего<ог() (яге (х, яге <у) сопя(( У индексация в стиле рог(гап Яке !(ег<((оиЫе> орега<ог() (яге (1) (ге(игп со(итп (1); ) Сздсе 1<ее<((оиЫе> орега<ог() (яге «) сопл< (ге(игп со!итп (<) ( ) Бйсе 1<ее<((оиВ(е> орега(ог() (з1ге <!) (ге(игп со!итп (() ( ) У индексация в стиле С Сз!ке 1<ее<((оиВ(е> орега<ог() (з(ге ( () сопл( (ге<ига со!итп (<) ( ) Ма<пхг орега(ог'= (((оиЫе) ча(аггау<((оиЫе>г аггау () (ге(игп *г; ) )( Здесь матричный класс Маратх построен на базе массива га1аггау.
Размерность мы «добавляем» здесь с помощью срезов. По мере необходимости мы можем рассматривать это представление как одномерное, двумерное, трехмерное и т.д. так же, как мы ввели умолчательное двумерное представление с помощью гоп () и со!итп () . Здесь яге < зг = г . з!ге ( ) (7(зг<2) ге<игл( яге <п = зг/2( з(ге < п2 = зг-п( Ьа(77 = г(зиЬ аггау (О, п) ); ЬаЦ2 = г(зиЬ аггау(п, п2) ) ( У... с!ат Маптх ( га1аггау<((оиЫе>* г; яге <а1, <(2; риВ((с: Ма(пх(яге <х, яге <у) ( Ма<пх(сопз<Маитха) ( Маипхз орега<ог= (сопя Ма(ггхз ) ( -Ма(г<х () (Ие!е(е»ч ) У хранит эл-ты поколонно, кок описано в 522.4.5 /У а(! — кол-во столбцов, ((2 - кол-во строк // внимание: умолчательного конструктора нет Глава 22.
Классы для математических вычислений о1!се 1(ег используется для обхода запрета на копирование здсе аггау. Я не могу воз- вратить зйсе аггау зйсе аггау<йоиЫе> гов(з(ге ((') (ге(игп (*г) [зйсе(1,й1,й2) ]; ) //еггог и поэтому я возвращаю итератор, содержащий указатель на га!аггау и собственно срез. Нам еще требуется тип «указатель на срез констант» — Сзйсе йег, чтобы отразить отличие в срезах для константных и неконстантных Мал гк (пйпе Я!се йег<йоиЫе> Ма(ггх: (гов (йге ( () ( ге(игп ойсе йег<йоиЫе> ( г, зйсе ((, й1, й2) ); (пйпе Сзйсе !(ег<йоиЫе> Ма(пх:: гов (з!ге ( () сопз( ( ге(игп Сзйсе йег<йоиЫе> ( г, з1(се (1, й1, й2) ); ) тйпе Я(се йег<йоиЫе> Ма(пх::со!итп (з!ге (() гешгп 5йсе йег<йоиЫе> (г,зйсе (!*й2,й2,1) ); (п(те С(1(се йег<йоиЫе> Ма(»Ь:: со(итп (гйге ( () сопи ( гегагп Сзйсе 1(ег<йоиЫе> (г, зйсе й*й2, й2, 1) ); Определение итератора Сзйсе йег идентично определению Я(се йег за исключением того, что возвращаются константные ссылки на элементы среза.
Остальные функции-члены довольно тривиальны: МаМх::Ма(г(х (з!ге (х, йге (у) ( // проверка осмысленности х и у й1 =х( й2 =у; г = пев га(аггау<йоиЫе> (х*у) ( ) йоиЫеа Ма(г1х::прего(ог() (иге (х, з!ге (у) ( ге(игп со!итп (х) (у] ' йоиЫе ти1(сопз( Сзйсе йег<йоиЫе>ь Ы, сопз( га!аггау<йоиЫе>а »2) ( йоиЫе гез = О; 2ог(з(ге ((' = О( (<г2лйге() ( 1««) гез»= г1((]»о2(1] ) ге(игп гез; га!аггау<йоиЫе> орега(ог* (сопл(Ма(гЫа т, сопз( га!аггау<йоиЫе>а г) 22.4, Векторная арифметика 793 га(аггау<доиые> гея (т.
Жт2 ( ) ); уог(я1яе !1= 0г 1<т.йт2() г 1++) гея[1] = ти1(т.голе(1), г) гегигп гея; ) МаМха Маигх:: орегагог* = (доиЫе д) ( (*г) *= л[) ге!ига *гййг Для индексации матрицы я использую выражение (14), поскольку операция () — единственная, и потому что такое выражение прекрасно знакомо всем специалистам-вычислителям. Концепция строки обеспечивает более знакомое (для программистов на С и С++) выражение [1] [1]: го(ау'(Магг!та т) ( т(1,2) = 5г гУ индексация в стиле гог(гап т.гош(!) (2) = б; т . гош (1) [2] = 7; т [1] (2) = 8! У нежелательный сиешанный стиль (работает) т [1] [2] = 9! 77 индексация в стиле Сл--ь Использование яйсе аггау в целях реализации индексации предполагает наличие хорошего оптимизатора кода. Обобшение этого подхода на случай и-мерных матриц произвольных элементов с разумным набором операций оставляется в качестве упражнения (В22.9[7]).
Возможно, ваша первая идея насчет двумерной матрицы может выглядеть примерно так: с1ат МаМх га[аггау<га[аггау<лгоиые» г; 77 ... Однако для типа га1аггау гарантируется лишь работа со скалярными элементами, так что нам придется писать что-то вроде гесяог<гесгог<ггоиые» г; и совсем не просто достичь эффективности и совместимости, необходимых для высокопроизводительных вычислений, не опускаясь на традиционный низкий уровень, представленный типом га1аггау и срезами. 22.4.7. Временные обьекты, копирование, циклы Если вы строите векторный или матричный класс, то скоро обнаружите три проблемы, решения которых от вас ожидают пользователи, которым требуется высокая производительность: 794 Глава 22. Классы для математических вычислений 1. Минимизация временных объектов.
2. Минимизация копирования матриц. 3. Минимизация вложенных циклов над одними и теми же данными. Эти проблемы не связаны напрямую с основной целью стандартной библиотеки. Здесь я только схематически опишу прием, позволяющий осушествить высоко- оптимизированные реализации. Рассмотрим Е/=М» У«)У, где 11, 1'и )У вЂ” векторы, а М вЂ” матрица. Наивная реализация предоставит временные векторы для М Уи М' У» 1У, и будет копировать результаты вычисления этих выражений. Более искусная реализация введет функцию вроде ти1 аИИ апИ аее!ап(в«1, ьМ, а У, в »У), которая обходится без временных векторов, не копирует то и дело элементы и, вообше, обрашается к элементам по минимуму. Столь глубокая оптимизация требуется лишь для нескольких типов выражений, так что простое решение проблемы состоит в том, чтобы предоставить не слишком сложную функцию вроде ти1 аИИ апИ ая(яп (), и пусть пользователь вызывает ее там, где важна эффективность.