|
Программирование >> Оптимизация возвращаемого значения
public: ArraySize(intnumElements):theSize(numElements) {} int size() const{return theSize;} private: int theSize; Array (int lowBound, int highBound) ; Array(ArraySizesize); Обратите внимание на изменения в объявлении. Здесь класс ArraySize размещен внутри Array, чтобы подчеркнуть, что он всегда используется вместе с классом Array. Класс ArraySize также объявлен как риЫ i с, благодаря чему доступ к нему открыт. Посмотрим, что сл5ится, если определить объект Array при помощи конструктора с единственным аргументом: Array<int> а (10) ; Ваши компиляторы генерируют вызов конструктора класса Array<int>, принимающий int в качестве аргумента, но такого конструктора не существует. Компиляторы могут преобразовать аргумент типа int во временный объект ArraySize, поскольку объект ArraySize - именно то, что нужно для конструктора Array<int>, и выполняют такие преобразования по своим обычным правилам. Эта процедура обеспечивает успешный вызов функции и сопутствующее создание объекта. Мысль о том, что объекты Array могут быть созданы с помощью аргументов типа int, успокаивает, но нужно еще убедиться, что запрещены нежелательные преобразования типа. Снова взглянем на этот код: booloperator==(const Array<int>&Ihs, const Array<int>& rhs); Array<int> a (10); Array<int>b(10); for (int i=0; i<10; + + i) if(a==b[i])... Вместо a должно стоять a [i] ; / / теперь это ошибка. Компиляторам нужен объект типа Array<int> справа от ==, чтобы можно было вызвать operator = = для объектов Array<int>, но конструктора, принимающего единственный аргумент типа int, не существует. Компиляторы не могут преобразовать int во временный объект ArraySize, а затем создать необходимый объект Array<int> из временного, потому что это потребует вызова двух преобразований, определенных пользователем: из int в ArraySize и из ArraySize в Array<int>. Такая последовательность преобразований запрещена, поэтому при попытке выполнить сравнение компиляторы сгенерируют ошибку. Использование класса ArraySize может показаться искусственным приемом, но на самом деле это частный случай более универсального метода. Классы типа ArraySize часто называют ргоосу-классами, потому что каждый объект такого класса соответствует другому объекту (замещает его). Объект ArraySize действительно замещает целое число, определяющее размерность создаваемого массива Array. Proxy-объекты предоставляют контроль над некоторыми сторонами поведения программы, в данном случае неявными преобразованиями типа, что невозможно получшъ другими способами, поэтому приглядитесь к ним попристальнее (см. правило 30). Однако прежде чем обратиться к proxy-классам, еще раз обратите внимание на следующий факт: разрешая компиляторам выполнять неявные преобразования типа, вы почти наверняка получше больше сложностей, чем положительных результатов, поэтому не создавайте функций преобразования типа, если не уверены в их необходимости. Правило 6. Различайте префиксную и постфиксную формы операторов инкремента и декремента Давным-давно (в конце 80-х) в далеком-далеком языке (С++ того времени) разницы между префиксными и постфиксными формами операторов + + и - не существовало. Программистам не хватало этой возможности, и С++ был дополнен разрешением перегружать обе формы инкрементных и декрементных операторов. Однако при этом возникла синтаксическая проблема, связанная с тем, что компилятор различает перегруженные функции по типу передаваемых аргументов, а префиксные и постфиксные формы инкрементных и декрементных операторов не имеют аргументов. Чтобы преодолеть синтаксическую ловушку, было принято волевое решение: постфиксные формы принимают аргумент типа int, а компиляторы при вызове этих функций по умолчанию Подставляют О в качестве аргумента: classUPInt { intнеограниченного размера, public: UPInt& operator++{); Префиксная форма ++, constUPIntoperator++{int) ; постфиксная форма++. UPInt&operator--(); Префиксная форма - , const UPInt & operator- - ( int) ; постфиксная форма -- . UPInt& operator + =(int); Оператор += для UPInt и int. UPInti; ++i; Вызывается i .operator+-t () . i + +; Вызывается i.operators+(0) . нужно подставить. Обратите внимание, что постфиксный оператор не использует переданный параметр. Это нормально. Единственное назначение параметра - обеспечить различие вызовов префиксной и постфиксной форм функций. Многие компиляторы генерируют предупреждения, если параметры, передаваемые функции, не используются в ее теле. Чтобы избежать таких предупреждений, можно не указывать имена неиспользуемых в теле функции параметров, как показано выше. Понятно, почему постфиксная форма инкрементного оператора должна возвращать объект (она возвращает сохраненное значение), но почему объект имеет атрибут const? Представим себе, что атрибут отсутствует. Тогда следующий код будет корректным: UPInti; i++++; Применяемпостфиксное увеличение дважды. Этот код аналогичен следующему: i.operator++{О).operator++(О); --i; Вызывается i.operator--(). i--; Вызывается i . operator--(0) . Это соглашение выглядит несколько странно, но вы привыкнете. Важен другой факт: префиксные и постфиксные формы операторов возвращают значения различного типа. В частности, префиксные формы возвращают ссылку, а постфиксные формы - объект с атрибутом const. Далее я буду говорить только о префиксной и постфиксной форме оператора ++, поскольку механизм работы оператора - аналогичен. Возможно, еще с тех времен, когда вы программировали на С, вы помните, что префиксная форма инкрементного оператора иногда называлась увеличить и подставить , а постфиксная форма - подставить и увеличить . Важно не забыть эти две фразы, потому что они почти полностью соответствуют формальной спецификации использования префиксной и постфиксной форм: / / Префиксная форма: увеличить и подставить. UPInt& UPInt::operator++() *this+=l; Увеличить, return*this; Подставить. Постфиксная форма:подставить иувеличить. const UPInt UPInt: : operator++ (int) { UPIntoldValue = *this; Запомнить. ++{*this); Увеличить, return oldValue; Вернуть то, что
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |