operator=( const Account &rhs )
{
// не надо присваивать самому себе
if ( this != &rhs )
{
// вызывается string::operator=( const string& )
_name = rhs._name;
_balance = rhs._balance;
}
return *this;
}
Чтобы запретить почленное копирование, мы поступаем так же, как и в случае почленной инициализации: объявляем оператор закрытым и не предоставляем его определения.
Копирующий конструктор и копирующий оператор присваивания обычно рассматривают вместе. Если необходим один, то, как правило, необходим и другой. Если запрещается один, то, вероятно, следует запретить и другой.
Упражнение 14.17
Реализуйте копирующий оператор присваивания для каждого из классов, определенных в упражнении 14.14 из раздела 14.6.
Упражнение 14.18
Нужен ли копирующий оператор присваивания для того класса, который вы выбрали в упражнении 14.3 из раздела 14.2? Если да, реализуйте его. В противном случае объясните, почему он не нужен.
14.8. Соображения эффективности A
В общем случае объект класса эффективнее передавать функции по указателю или по ссылке, нежели по значению. Например, если дана функция с сигнатурой:
bool sufficient_funds( Account acct, double );
то при каждом ее вызове требуется выполнить почленную инициализацию формального параметра acct значением фактического аргумента-объекта класса Account. Если же функция имеет любую из таких сигнатур:
bool sufficient_funds( Account *pacct, double );
bool sufficient_funds( Account &acct, double );
то достаточно скопировать адрес объекта Account. В этом случае никакой инициализации класса не происходит (см. обсуждение взаимосвязи между ссылочными и указательными параметрами в разделе 7.3).
Хотя возвращать указатель или ссылку на объект класса также более эффективно, чем сам объект, но корректно запрограммировать это достаточно сложно. Рассмотрим такой оператор сложения:
// задача решается, но для больших матриц эффективность может
// оказаться неприемлемо низкой
Matrix
operator+( const Matrix& m1, const Matrix& m2 )
{
Matrix result;
// выполнить арифметические операции ...
return result;
}
Этот перегруженный оператор позволяет пользователю писать
Matrix a, b;
// ...
// в обоих случаях вызывается operator+()
Matrix c = a + b;
a = b + c;
Однако возврат результата по значению может потребовать слишком больших затрат времени и памяти, если Matrix представляет собой большой и сложный класс. Если эта операция выполняется часто, то она, вероятно, резко снизит производительность.
Следующая пересмотренная реализация намного увеличивает скорость:
// более эффективно, но после возврата адрес оказывается недействительным
// это может привести к краху программы
Matrix&
operator+( const Matrix& m1, const Matrix& m2 )
{
Matrix result;
// выполнить сложение ...
return result;
}
но при этом происходят частые сбои программы. Дело в том, что значение переменной result не определено после выхода из функции, в которой она объявлена. (Мы возвращаем ссылку на локальный объект, который после возврата не существует.)
Значение возвращаемого адреса должно оставаться действительным после выхода из функции. В приведенной реализации возвращаемый адрес не затирается:
// нет возможности гарантировать отсутствие утечки памяти
// поскольку матрица может быть большой, утечки будут весьма заметными
Matrix&
operator+( const Matrix& m1, const Matrix& m2 )
{
Matrix *result = new Matrix;
// выполнить сложение ...
return *result;
}
Однако это неприемлемо: происходит большая утечка памяти, так как ни одна из частей программы не отвечает за применение оператора delete к объекту по окончании его использования.
Вместо оператора сложения лучше применять именованную функцию, которой в качестве третьего параметра передается ссылка, где следует сохранить результат:
// это обеспечивает нужную эффективность,
// но не является интуитивно понятным для пользователя
void