}
Теперь почленная инициализация по умолчанию для класса Account корректно обрабатывает выделение и освобождение памяти для _name, но все еще неверно копирует номер счета, поэтому приходится кодировать явный копирующий конструктор. Однако приведенный ниже фрагмент не совсем правилен. Можете ли вы сказать, почему?
// не совсем правильно...
inline Account::
Account( const Account &rhs )
{
_name = rhs._name;
_balance = rhs._balance;
_acct_nmbr = get_unique_acct_nmbr();
}
Эта реализация ошибочна, поскольку в ней не различаются инициализация и присваивание. В результате вместо вызова копирующего конструктора string мы вызываем конструктор string по умолчанию на фазе неявной инициализации и копирующий оператор присваивания string – в теле конструктора. Исправить это несложно:
inline Account::
Account( const Account &rhs )
: _name( rhs._name )
{
_balance = rhs._balance;
_acct_nmbr = get_unique_acct_nmbr();
}
Самое главное – понять, что такое исправление необходимо. (Обе реализации приводят к тому, что в _name копируется значение из rhs._name, но в первой одна и та же работа выполняется дважды.) Общее эвристическое правило состоит в том, чтобы по возможности инициализировать все члены-объекты классов в списке инициализации членов.
Упражнение 14.13
Для какого определения класса скорее всего понадобится копирующий конструктор?
1. Представление Point3w, содержащее четыре числа с плавающей точкой.
2. Класс matrix, в котором память для хранения матрицы выделяется динамически в конструкторе и освобождается в деструкторе.
3. Класс payroll (платежная ведомость), где каждому объекту приписывается уникальный идентификатор.
4. Класс word (слово), содержащий объект класса string и вектор, в котором хранятся пары (номер строки, смещение в строке).
Упражнение 14.14
Реализуйте для каждого из данных классов копирующий конструктор, конструктор по умолчанию и деструктор.