Г. Шилтд - Самоучитель C++ (DJVU) (1114955), страница 25
Текст из файла (страница 25)
Если параметр ))ои не задан, его значение по умолчанию равно — (, что говорит функции о необходимости повторно использовать его предыдущее значение. ()1пс1и<(е <1авТгеаж> ()1пс1ис(е <сссуре> паупер патпеярасе ясо; сопел 1пс 1дпоге = 0; сопев 1пс пррег = 1; сопяс 1пс 1оыег = 2; уоЫ рггпс(слав *я, 1пс 'пои = -1); 1пС тата() ргапе ( "привет Ы', 1споге); Перегрузка Функций Глава рк1п~(вПривет ~п", пррег); ргйпс (" Привет хпв); // продолжение вывода в верхнем регистре рг1пС (" Привет М", 1оиег); рг1пс (вЭтоконец 'хп"),' // продолжение вывода // в нижнем регистре геппгп О; /* Печать строки в заданном регистре.
Использование заданного последним регистра, если он не задан. */ чо)с( рг1пс (сЬаг *я, Тп Пои) ! ягасус Тпс о1бсаяе = Тупоге; повторять работу с прежним регистром, если новый не задан 1Г ()том<О) Пои =- о1осаве; ы)т11е (*д) яи1ссЬ (Ьои) ( саве иррег: сопя « (с)тат) 1опррег (*я); )згеа)<; саяе 1оыег: сопя « (с)таг) со1оиег (*я) т Ьге а)с; аеГ 1ЬР т « '; в~+; о1асаяе = )том; Эта программа выводит следующее: Привет ПРИВЕТ ПРИВЕТ привет зто конец 5.
Ранее в этой главе мы рассматривали общую форму конструктора копий. В этой общей форме имелся только один параметр. Однако вполне возможно создавать конструкторы копий, получающие дополнительные аргументы, если только это аргументы по умолчанию. Например, вполне приемлема следующая форма конструктора копий: п1ус1аяв (сопя". атус1авя ЯоЬ3, 1п( х=О) ( // тело конструктора ) Самоучитель С..ч. Поскольку первый аргумент является ссылкой на копируемый объект, а все остальные — это аргументы по умолчанию, эту функцию можно квалифицировать как конструктор копий. Такая гибкость позволяет создавать самые разнообразные конструкторы копий. 6. Хотя аргументы по умолчанию являются мощным и удобным инструментом, ими нельзя злоупотреблять.
Несомненно, что при правильном применении аргументы по умолчанию позволяют функции выполнять свою работу эффективным и простым по реализации образом. Однако так происходит лишь тогда, когда переданное по умолчанию значение имеет смысл. Например, если аргумент, используемый в девять или десять раз чаще других, передать функции по умолчанию, то, очевидно, это неплохо. Однако в случае, если нет значения, используемого чаще других, или нет выгоды от аргумента по умолчанию в качестве флага, то нет большого смысла передавать что-либо по умолчанию. Фактически, обеспечение передачи аргумента по умолчанию, когда это не вызвано необходимостью, ограничивает возможности вашей программы и вводит в заблуждение всех пользователей такой функции.
Как и при перегрузке функций, хороший программист в каждом конкретном случае всегда сумеет определить, стоит или нет пользоваться аргументом по умолчанию. 1. В стандартной библиотеке С++ существует функция я$г1о!О, имеющая следующий прототип: 1опя еЕгсо1 (аспас спаг ~еЕаа"Е, сопаГ **вам, )пГ Ьаее) Функция преобразует обозначающую число строку, на которую ссылается указатель 5)агб в длинное целое. Число Ьа5е задает основание системы счисления этого числа. При возвращении функцией своего значения указатель елд ссылается на символ в строке, следуюший сразу за последней цифрой строки.
Возвращаемое длинное целое эквивалентно тому числу, которое записано в строке. Диапазон значений Ьа5е от 2 до 38. Однако наиболее часто основание системы счисления равно 1О. Создайте функцию туз)гто11), работающую точно так же, как и функция вЫо10, но аргумент !О должен передаваться параметру Ьазе по умолчанию. (Свободно пользуйтесь функцией вЫо!9 для фактического преобразования. Для этого в программу требуется включить заголовок <еМ11Ь>.) Покажите„ что ваша версия работает правильно.
2. Что неправильно в следующем прототипе функции? спаг *Е<сйаа *р, )пг х = О, с)гаг *ч) 3. В большинстве компиляторов С++ применяются нестандартные функции, управляющие позиционированием курсора и другими аналогичными деист- 165 Глава 5. Перегрузка функций виями.
Если в вашем компиляторе применяются такие функции, создайте функцию шус!гео!О, которая стирает строку, начиная от текущей позиции курсора до конца строки. Передайте этой функции параметр, задающий число стираемых позиций. Если параметр не задавать, то по умолчанию должна стираться вся строка. В противном случае должно стираться число символьных позиций, заданное параметром. 4. Что неправильно в следующем прототипе функции с аргументом по умолчанию? !п1 т(йп1 сопп1, !п1 шах = соепТ! 5.5. Перегрузка и неоднозначность При перегрузке возможно внесение неоднозначности в программу.
Неоднозначность !атЬ|яийу), вызванная перегрузкой функций, может быть введена в программу при преобразовании типа, а также при использовании параметров-ссылок и аргументов по умолчанию. Некоторые виды неоднозначности вызываются самой перегрузкой функций. Другие виды связаны со способом вызова перегруженных функций. Чтобы программа компилировалась без ошибок, от неоднозначности необходимо избавиться.
~"Л~рм~~~ ~ !. Один из наиболее частых видов неоднозначности вызывается правилами преобразования типа в С++. Как вы знаете, при вызове функции с аргументом, тип которого совместим (но не аналогичен) с типом параметра, которому он передается, тип аргумента по умолчанию преобразуется в тип параметра. Об этой операции иногда говорят как о приведении типа (~ура рготойол). Приведение типа — это такой вид преобразования типа, который позволяет некоторым функциям, например ритсЬагО, вызываться с символьным параметром, даже тогда, когда аргумент функции имеет тип 1п!. Однако в некоторых случаях это преобразование типа при перегрузке функций вызовет ситуацию неоднозначности. Чтобы понять, как это происходит, исследуем следующую программу: Эта програыла содержит ошибку неоднозначности 41пстабе <1овскеат> свтгд пашеарасе вдй; Й1оаб т (Й1оаб .т) ге~ага т ! 2.0; ) /бб Самоучитель С++ ЦоцЬ1е т (ооцЬ1е 1) гебцгп 1 / 3.0; 1пс гааьп ( ) Й1оаб х = 10.09; боцЬ1е у = 10.09; соцс « 1(х); // пет неоднозначности // используется функция Й (г1оас) соцс « Й (у) ~ // нет неоднозначности используется функция Г (с(опЬ1е) соцс « Й(10); // неоднозначность О куда преобразовать 109 в значение типа ооцЬ1е или б1оабт кесцгп 0; Как указано в комментариях к функции п)а1п(), компилятор в состоянии выбрать правильную версию функции Г(), если она вызывается либо с переменными типа доцЫе, либо с переменными типа Поа1.
Однако что случается, если она вызывается с целым? Какую функцию вызовет компилятор Г(Лоан) или Г(доиЫе)? (Оба преобразования правильны!) И в том, и в другом случае правильно "привести" тип 1п1 либо к типу Поа1, либо к типу доиЫе. Таким образом, возникает ситуация неоднозначности. Этот пример выявляет также то, как неоднозначность может проявляться при вызове перегруженных функций. Очевидно, что сама по себе неоднозначность не присуща перегруженным версиям функции ((), пока каждая вызывается с аргументом соответствующего типа.
2. Другой пример перегрузки функции, которая сама по себе не должна приводить к неоднозначности. Тем не менее, при вызове с аргументом неправильного типа, правила преобразования типа С++ создают ситуацию неоднозначности. // Эта программа неоднозначна ()1пс1цбе <1овекеага> иь!пя пагпеарасе вес(г уо(г1 1(ппв1дпей с)гаг с) соШ «с," уоЫ Г(сЬаг с) ( сонг « су 767 Глава функций Перегрузка (пт гпа(п О ( (('с'); 1(8б)," /У какая версия функции й() внзываетсяу тесн и О; Когда функция (О вызывается с числовой константой 86, компилятор не может понять, какую версию функции вызвать: 1((жвщпед с)(аг) или ((с)таг). Оба преобразования одинаково правильны, что и ведет к неоднозначности.
3. Один из видов неоднозначности проявляется, если вы пытаетесь перегрузить функции, единственным отличием которых является то, что одна использует параметр-ссылку, а другая параметр-значение по умолчанию. В рамках формального синтаксиса С++ у компилятора нет способа узнать, какую функцию вызвать. Запомните, что нет синтаксических отличий между вызовом функции по значению и вызовом функции по ссылке. Например: // Эта программа неоднозначна () тпс1ибе <1овстеатв> ив1пя паптеврасе аеФ (пт т((пт а, (пт Ь) гетпгп а -ь Ь„. 1 // здесь внутренняя неоднозначность 1п( в (зпс а, 1п( аЬ) ( гетигп а — Ь; нт( тазп () ( тптх=г, у=Я; соо( « Г(х, у); // какую версию й () вызвать? гетигп О; Здесь вызов функции Г(х, у) неоднозначен, поскольку вызвана может быть любая версия функции.