Выбрать главу

(a) class BinStrTreeNode {

public:

// ...

private:

string _value;

int _count;

BinStrTreeNode *_leftchild;

BinStrTreeNode *_rightchild;

};

(b) class BinStrTree {

public:

// ...

private:

BinStrTreeNode *_root;

};

(c) class iMatrix {

public:

// ...

private:

int _rows;

int _cols;

int *_matrix;

};

(d) class theBigMix {

public:

// ...

private:

BinStrTree _bst;

iMatrix _im;

string _name;

vectorMfloat *_pvec;

};

Упражнение 14.15

Нужен ли копирующий конструктор для того класса, который вы выбрали в упражнении 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, то почленное присваивание по умолчанию