Нужен ли копирующий конструктор для того класса, который вы выбрали в упражнении 14.3 из раздела 14.2? Если нет, объясните почему. Если да, реализуйте его.
Упражнение 14.16
Идентифицируйте в следующем фрагменте программы все места, где происходит почленная инициализация:
Point global;
Point foo_bar( Point arg )
{
Point local = arg;
Point *heap = new Point( global );
*heap = local;
Point pa[ 4 ] = { local, *heap };
return *heap;
}
14.7. Почленное присваивание A
Присваивание одному объекту класса значения другого объекта того же класса реализуется почленным присваиванием по умолчанию. От почленной инициализации по умолчанию оно отличается только использованием копирующего оператора присваивания вместо копирующего конструктора:
newAcct = oldAcct;
по умолчанию присваивает каждому нестатическому члену newAcct значение соответственного члена oldAcct. Компилятор генерирует следующий копирующий оператор присваивания:
inline Account&
Account::
operator=( const Account &rhs )
{
_name = rhs._name;
_balance = rhs._balance;
_acct_nmbr = rhs._acct_nmbr;
}
Как правило, если для класса не подходит почленная инициализация по умолчанию, то не подходит и почленное присваивание по умолчанию. Например, для первоначального определения класса Account, где член _name был объявлен как char*, такое присваивание не годится ни для _name, ни для _acct_nmbr.
Мы можем подавить его, если предоставим явный копирующий оператор присваивания, где будет реализована подходящая для класса семантика:
// общий вид копирующего оператора присваивания
className&
className::
operator=( const className &rhs )
{
// не надо присваивать самому себе
if ( this != &rhs )
{
// здесь реализуется семантика копирования класса
}
// вернуть объект, которому присвоено значение
return *this;
}
Здесь условная инструкция
if ( this != &rhs )
предотвращает присваивание объекта класса самому себе, что особенно неприятно в ситуации, когда копирующий оператор присваивания сначала освобождает некоторый ресурс, ассоциированный с объектом в левой части, чтобы назначить вместо него ресурс, ассоциированный с объектом в правой части. Рассмотрим копирующий оператор присваивания для класса Account:
Account&
Account::
operator=( const Account &rhs )
{
// не надо присваивать самому себе
if ( this != &rhs )
{
delete [] _name;
_name = new char[strlen(rhs._name)+1];
strcpy( _name,rhs._name );
_balance = rhs._balance;
_acct_nmbr = rhs._acct_nmbr;
}
return *this;
}
Когда один объект класса присваивается другому, как, например, в инструкции:
newAcct = oldAcct;
выполняются следующие шаги:
* Выясняется, есть ли в классе явный копирующий оператор присваивания.
* Если есть, проверяются права доступа к нему, чтобы понять, можно ли его вызывать в данном месте программы.
* Оператор вызывается для выполнения присваивания; если же он недоступен, компилятор выдает сообщение об ошибке.
* Если явного оператора нет, выполняется почленное присваивание по умолчанию.
* При почленном присваивании каждому члену встроенного или составного члена объекта в левой части присваивается значение соответственного члена объекта в правой части.
* Для каждого члена, являющегося объектом класса, рекурсивно применяются шаги 1-6, пока не останутся только члены встроенных и составных типов.
Если мы снова модифицируем определение класса Account так, что _name будет иметь тип string, то почленное присваивание по умолчанию
newAcct = oldAcct;
будет выполняться так же, как при создании компилятором следующего оператора присваивания:
inline Account&
Account::
operator=( const Account &rhs )
{
_balance = rhs._balance;
_acct_nmbr = rhs._acct_nmbr;
// этот вызов правилен и с точки зрения программиста
name.string::operator=( rhs._name );
}
Однако почленное присваивание по умолчанию для объектов класса Account не подходит из-за _acct_nmbr. Нужно реализовать явный копирующий оператор присваивания с учетом того, что _name - это объект класса string:
Account&
Account::