Бьерн Страуструп. Язык программирования С++. Специальное издание (2011) (1004033), страница 75
Текст из файла (страница 75)
Ачьтернативой является семантика указателей, при которой после в1=в2 изменение х1 влекло бы за собой и изменение х2. Для типов с обычными арифметическими операциями, таких как комплексные числа, вектора, матрицы и строки, я предпочитаю семантику значений. Однако чтобы обеспечить семантику значений, класс Ю(г!пя реализован в виде дескриптора (Ьап(!!е) к классу представления (гергехеп(айви с!авв), которое копируется лишь при необходимости: Глава 11 Перегрузка операций 364 гер = иеи Ягер ( О, " " ); ) //умолчательное значение - пустая строка Я<г!пО:: Я<<<ил (соим Я<г!пяа х) ( х. гер->п-н-; гер = х.гер; ) Я<г!иО:: -Я<с<ил ( ) ( <1 (--гер->и==О) «е!е<е гер; ) //разделяемое представление Я<г(пяь Я<пил<:орега<ог= (соп«< Я<пийа х) ( х.
гер->пьь; //защита от "з<=«<" <! (--гер->и==О) «е<е<е гер< гер = х.гер< // разделяемое представление ге<игл *<!<1«< ) Операции псевдокопирования с аргументами типа сои«< сйаг* позволяют работать со строковыми литералами: Я<пил:: Я<г<пя (сои«< с«аг* «) ( гер = пезв Ягер («<<1еп («), «) ) Я<г!пО« Я<г!пО<:орега<ог= (соим сваг' з) ( Ц'(гер->и==1) гер->а««!Оп («<г1еп («), «) < е<«е ( гер->и--< гер = пе<г Ягер («<г<еп («), «) < // используем новый Зер ) ге<иго *<«1«< / используем старый Я ер ей<««Я<<<ил ( //... гоЫ с«ес«(<и<1) соп«< (<1 (!<О ) ( гер->«с<=!) <«го<в 1(аллее () < ) Проектирование операций доступа к строкам является непростой материей, поскольку в идеале хотелось бы доступа в привычной форме (операцией () ), максимально эффективного и с проверкой диапазона индексов.
К сожалению, невозможно обеспечить все эти свойства одновременно. Мой выбор таков: эффективные операции без проверки диапазона со слегка неудобной формой записи плюс менее эффективные операции с проверкой диапазона и со стандартной формой записи: 11.12 Класс строк 365 сЬа геохи((п! !) соаз! (гегагп гер->з [(1; ) кого' тг(ге (тп! (, слог с) (гер=гер->ее< опп сору () ) гер->и [!) =с) ) Сгеу орегазог [) ((п! !) (сьесЬ (!); ге!ига Сге((~гл(з, !); ) слог прего!ос[) ((п! !) сопл! (слесЬ(!) ! гезигп гер->в [!1) ) !пзз(зе() сопл! (ге!ига гер->за() р ): Идея состоит в том, чтобы использовать операцию [) с проверкой диапазона в обшем случае, но предоставить пользователю оптимизированную возможность однократной проверки диапазона для серии обрашений.
Например: !пг Ьазл (сопя! Юзгупяз з) ( ии Ь = з.геев(0); сопл! (п! тах = з. з!зе ( ); уог((п! ! = 1; !<тах; !ее) Ь к= з.геев(!) »2; гуобращение к з без проверки ге!ига Ь; ) Определять работу операции [ ) для чтения и записи сложно в тех случаях, когда не допустимо просто вернуть ссылку и позволить делать пользователю, что ему вздумается. В нашем случае такое решение не подходит, поскольку я определил класс Юзг!пя таким образом, что между несколькими экземплярами класса Ютг!пп, которые присваивались, передавались в качестве аргументов и т.д., представление разделяется до тех пор, пока не потребуется фактическая запись.
Тогда, и только тогда, представление копируется. Этот прием называют копированием по фактической записи [сору-оп-ьиг[ге). Само копирование выполняется функцией Югг!пя::Югер:: яе! опп сору(). Чтобы можно было функции доступа сделать встраиваемыми, нужно помешать их определения в области видимости Югер, что в свою очередь можно обеспечить либо помешением определения Югер внутри определения Югг!пя, либо определением функций доступа с модификатором !и!!пе вне Юзг!пя и после ЮУг!пя:: Югер [З11.14 [2) ). Чтобы различать чтение и запись, ЮМпп:: орегатог [) () возвращает Сгеу, если он вызван для неконстантного обьекта.
Тип Сге!'ведет себя как сЬагз, за исключением того, что он вызывает Ю)зтпя:: пг!зе () для операции присваивания: с(азз Юзг(пе:: Сге! Р ссылка на зЯ ( Яепг! с(азз Юп (пе; Юге[паз з; !пС !( Сге/(Юзг(пяз зз, (п! й): «(зз), !(Ь) ( ) Сге)(сопл! Сге!з г): з(г.з), 1(г.!) ( ) Сге(() ) У не определяется [не используется! риЬбс: орегагог сваг() сопи! ( з.сьесЬ(!); ге!ига з.геев(!); ) ~У выдает значение Глава 11. Перегрузка операций Збб гУ изменяет значение гоИ арета(от=(сйат с) ( з.нейе(б с); )' Например: то(г! ((Ят!пО з, сонм Ягт!пль г) ( сйаг с1=з [1); з[1) = 'с'; тт с! =я. арета(огЦ(!).арета(ог сйат() о' я. арета(ого(1).
арета(от=(с ) сйаг с2 = г[1) г Ус2 = г.арета(отД(1) с [1) = 'Ф' г й еттот ирнсваивание для пон-!та!ие сйат, г.орегагогО(1) = )!' ) с!азз Язг!пе ( l/, Яг(пдь орете(оге = (соим Бзг!пль ) Бзт(идь орегагогь= (сопя! сйаг*); )г!епг! отгеать орегазот«(озпеать, сопя! озт(прь); Яепй !я!гент ь орегагот» ((я!гент ь, К!г!ивь ); 1т!еМ Ьоо! орегазог== (соиз! Бтг!прь х, сопят сйаг* з) ( гесагп язтсн~р (х.гер->я, з) == Ог 1т(еиИ Ьоо! орегасог== (сопзт Ятг!пдь х, сопят Ят!иль у) ( тенин тгстр(х.гер->з,у.гер->з) == О; гг!епИ Ьоо! орегагог! = (сопя! Ят!пль х, сопи! сйаг* з) ( ге!ига я!гетр (х. гер->з, з) ! = 0( ) 1т!епИ Ьоо1 орегатог! = (соиз! Язт!иль х, сонм Бтт(пдь у) ( ге!оси з(гетр (х.гер->з,у. тер->з) ! = 0; ) Яг!пл прего!от+ (сопя! Ятт!пкь, сонм Ят!иль ) Язг!ил орегазог+ (сопз! Ятт!пдь, соим сйат*); Операции ввода/вывода я оставляю в качестве упражнения.
Вот пример простого клиентского кода, использующего операции класса Ь)г!щ Яг!пО ((азт!пО а, Ятт!пк Ь ) ( а[2) = 'х'; сйаг с = Ь [3); сои!« "(и1': "«а« ' ' «Ь« ' ' «с« '~и'; гезиги Ь „. 1п! та!п () ( ЯЬ!одх, у; Обратите внимание на то, что для неконстантного объекта з. орегагог[) (1) есть Сге1(з, 1) . Для полноты я снабдил класс Яг!п0 набором полезных функций: 367 )1.)3. Советы сои!«" Р!сазе еп<ее пчо е!с!аде'~ и "; с!п»х»у; сои! « п(прис: " «х « ' ' « у « ' )и ' ! Яге(пя с = х; у = !'(х,у) ) !Г(х! = с) сош« "х соееирсег(! ',и" ! х(0! = '! '; г! (х == с) сои!« пни!ге)аг!ед! чпп; сои!«< "ех(г: " « х « ' ' «у « ' ' «с « ' ~п'; ) В классе 5Мпя вы можете обнаружить отсутствие многих возможностей, которые считаете важными или даже необходимыми.
Например, вам может недоставать возможности представления содержимого в виде С-строки (011.14[101, глава 20). 11.13. Советы 1. Перегружайте операции для имитации традиционных форм записи; 0! !.!. 2. Операнды больших размеров передавайте как констан~пные ссылки; 01!.6. 3. Для возвратов большого размера рассмотрите возможность оптимизации; в ! !.6. 4.
Предпочитайте умолчательное копирование от компилятора, если оно подходит для вашего класса; 011.3.4. 5. Переопределите или запретите умолчательное копирование от компилятора, если оно не подходит для вашего класса; 011.2.2. 6. Предпочитайте функции-члены, если требуется прямой доступ к представлению класса; 011,5.2. 7. Предпочитайте функциям-членам глобальные функции, если не требуется прямой доступ к представлению класса; 011.5.2. 8. Используйте пространства имен для связывания класса с функциями поддержки; 011.2.4.
9. Используйте глобальные функции для реализации симметричных операций; 01!.3.2. 10. Для индексации многомерных массивов используйте операцию (); 011.9. 11. Снабжайте конструктор с одним аргументом для задания размера модификатором ехр!!с!г, в11.7.1. 12. В обычных случаях предпочитайте стандартный класс зптп» (глава 20) своим собственным разработкам; 011.12. 13. Будьте осторожны, определяя неявные приведения типа; 011.4. 14.
Для реализации операций, требуюших !ча)це в качестве левого операнда, используйте функции-члены; 011.3.5. Глава ) ) Перегрузка операций Збй 11.14. Упражнения (*2) В следующей программе, какие преобразования выполняются в каждом выражении? лсгисс Х ( сис с'; Х(1ис) с Х орегасоге (сис) Фгисс у ( сис!) У(Х)с у орегагогч (Х) орегасог си с ( ); )с ехгегп Х орегагог* (Х, У) ехсеги сисТ(Х) с Хх= 1( с'у = х) сис с' = 2( си с таси ( ) ( 1-г10; х-~у+1( 1(У) ' ) у+10; у+10 ус х*хэсс Т(7)с угу; 10беу; риисс: Модифицируйте программу так, чтобы при запуске она выводила значения допустимых выражений. (*2) Завершите и отгестируйте класс Ясгсиа из 511.12.
(*2) Определите класс ИЧТ, который ведет себя в точности как шс. Подсказка: определите ИЧТ::орегасог спс(). (*1) Определите класс 11ИЧТ, который ведет себя как сис, за исключением того, что допустимыми операциями являются лишь ч, — (унарные и бинар- ные), *, 7 и з. Подсказка: не определяйте ЯИЧТ: сорегасог шс() . ('3) Определите класс ПРИЧТ, который ведет себя как КИЧТ, но для представ- ления чисел использует по крайней мере 64 бита. ("4) Определите класс с арифметикой произвольной точности. Протестируй- те этот класс, вычислив факториал от 1000. Подсказка: вам потребуется управление памятью вроде того, что было сделано для класса 5(г1и».
(*2) Для класса Бишй определите внешний итератор: с(аэл 5сгсп0 пег су ссылается на строку и элемент строки 11 14. Упражнения Збй 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 5)г)ле йег(Я)г)пяа в); //итератор длл л сйага лехе(); //ссылка на следующий элемент // иные операции по вашему выбору )' Сравните это по удобству, стилю программирования и эффективности с внутренним итератором для Ягг)лл (в смысле понятия текушего элемента 5)ггля и операций над этим элементом). (*1,5) Для строкового класса определите операцию выделения подстроки с помошью () .
Какие еше операции вам нужны для работы со строками? (*3) Разработайте класс ЮГг)лл таким образом, чтобы операцию взятия подстроки можно было использовать в левой части операции присваивания. Сначала ограничьтесь версией, где длины подстроки и присваиваемой строки одинаковые. Затем напишите версию с разными длинами. (*2) Напишите операцию для класса ЮЖл», позволяюшую получить содержимое в виде С-строки, Обсудите все за и против способа реализации этой операции в виде операции приведения типа. Обсудите возможные альтернативы выделения памяти под С-строку.