Информационный сайт

 

Реклама
bulletinsite.net -> Книги на сайте -> Программисту -> Мизрохи С.В. -> "Turbo Pascal и объектно-ориентированное программирование" -> 45

Turbo Pascal и объектно-ориентированное программирование - Мизрохи С.В.

Мизрохи С.В. Turbo Pascal и объектно-ориентированное программирование — М.: Финансы и статистика , 1992. — 192 c.
ISBN 5-279-00903-2
Скачать (прямая ссылка): efektispolzc2000.djvu
Предыдущая << 1 .. 39 40 41 42 43 44 < 45 > 46 47 48 49 50 51 .. 105 >> Следующая

Очевидно, нет никаких оснований полагать, что такой объект существова. до вызова operator*. Например, если у вас есть
Rational а(1, 2); /Ia= 1/2. Rational b(3, 5); //b=3/5. Rational с = a * b; // с должно равняться 3/10.
кажется неразумным ожидать, что уже существует рациональное число со значением одна десятая. Если operator* будет возвращать такое число, он должен создать его самостоятельно.
Функция может создать новый объект только двумя способами: в стеке или в куче. Создание в стеке совершается посредством определения локальной переменной. Используя эту стратегию, вы пытаетесь записать operator* следующим образом:
// Первый способ написать эту функцию неправильно.
inline const Rationalst operator*(const Rational& lhs, const Rational&
rhs)
{
Rational result(lhs.n * rhs.n, lhs.d * rhs.d); return result;
}
I-
oi
равило 23
101
Этот подход можно отвергнуть сразу, поскольку вашей целью было избежать вызова конструктора, a result должен быть создан подобно любому другому объекту. Кроме того, эта функция порождает и более серьезные проблемы, поскольку возвращает локальный объект - ошибка, более подробно рассмотренная в правиле 31.
Таким образом, вам предоставлена лишь возможность создания объекта в куче и возвращения его ссылки. Объекты в куче создаются с использованием new. Вот как мог бы выглядеть operator* в этом случае:
/ / Второй способ написать эту функцию неправильно.
inline const Rational& operator*(const Rational& lhs, const Rational& rhs) {
Rational *result = new Rational(lhs.n * rhs.n, lhs.d * rhs.d); return *result;
Да, вам все же придется расплачиваться за вызов конструктора, поскольку память, выделяемая new, инициализируется вызовом соответствующего конструктора (см. правило 5), но теперь встает другая проблема: как будет вызван delete для объекта, созданного вами с использованием new?
По правде говоря, это гарантирует утечку памяти. Даже если пользователя, вызвавшего operator*, можно было бы убедить взять адрес возвращаемого функцией результата и использовать для него delete (такая вероятность чрезвычайно мала; правило 31 показывает, как должен был бы выглядеть этот код), сложные выражения приводили бы к созданию безымянных временных объектов, никак не доступных программисту. Например, в
Rational w, х, у, z; w = X * у * z;
оба вызова operator* создают безымянные временные объекты, которые не видны программисту, а следовательно, не могут быть удалены (и снова см. правило 31).
Но предположим, что вы умнее среднего программиста. Возможно, вы обратили внимание, что недостатком обоих подходов, как со стеком, так и с кучей, является необходимость вызова конструктора для каждого результата, возвращаемого Функцией operator*. Возможно, вы еще не забыли о своем первоначальном желании избежать таких вызовов. Вероятно, вы знаете способ избежать всех вызовов конструктора, кроме одного. Вполне допустимо, что вы придумали следующую реализацию функции operator*, возвращающую ссылку на статический объект Rational, определенный внутри функции:
/ / Третий способ написать эту функцию неправильно.
inline const Rational& operator*(const Rationale lhs, const Rational& rhs) {
static Rational result; // Статический объект,
Il на который возвращается ссылка. Каким-либо образом перемножьте lsh и rsh и поместите результат в переменную result return result;
}
102
Классы и функции
Это выглядит многообещающе, хотя, попытавшись реализовать на С++ псевдокод инициализации, показанный выше, вы обнаружите, что практически невозможно придать необходимое значение result, не вызывая конструктор Rational, а стремление избежать такого вызова, собственно, и послужило поводом для всей этой деятельности. Более того, давайте предположим, что ваш маневр будет иметь успех - и все же никакой острый ум не сможет в конечном счете спасти эту программу, родившуюся под несчастливой звездой.
Для того чтобы вскрыть причину, давайте рассмотрим следующий совершенно разумный код:
// operator== для объектов Rational.
bool operator==(const Rational& lhs, const Rational& rhs) ; Rational a, b, c, d;
if ((a * b) == (c * d) ) {
действия, необходимые в случае, если произведения равны } else {
действия, необходимые в противном случае
}
Теперь обратите внимание, что выражение ( (a*b)==(c*d) ) всегда равняется true, независимо от значений а, Ь, с, Hd!
Легче всего найти объяснение такому неприятному поведению, переписав проверку на равенство в эквивалентной функциональной форме:
if (operator==(operator*(a, b) , operator*(с, d) ) )
Заметьте, что, когда вызывается operator==, всегда уже присутствуют два активных вызова функции operator*, каждый из которых будет возвращать operator* ссылку на статический объект Rational. Таким образом, operator== будет сравнивать статический объект Rational, определенный в функции operator*, со значением статического объекта Rational внутри operator* той же функции. Стоило бы удивиться, если бы при сравнении они не были равны всегда.
При некотором везении этого может быть достаточно, чтобы убедить вас, что возвращать ссылку из функции, подобной operator*, - пустая потеря времени, но я не настолько наивен, чтобы полагаться на везение. Кое-кто из вас в настоящий момент думает: «Хорошо, - если не достаточно одного статического объекта, может, для этого подойдет статический массив...»
Предыдущая << 1 .. 39 40 41 42 43 44 < 45 > 46 47 48 49 50 51 .. 105 >> Следующая
Реклама
Авторские права © 2009 AdsNet. Все права защищены.
Rambler's Top100