Программирование >>  Инициализация объектов класса, структура 

1 ... 228 229 230 [ 231 ] 232 233 234 ... 395


inline Account::

Account( const Account srhs )

решить проблему псевдонима указателя name = new char[ strlen(rhs. name)+1 ];

strcpy( name, rhs. name );

решить проблему уникальности номера acct nmr = get unie acct nmbr() ;

счета

копирование этого члена и так работает balance = rhs. balance;

Альтернативой написанию копирующего конструктора является полный запрет почленной инициализации. Это можно сделать следующим образом:

1. Объявить копирующий конструктор закрытым членом. Это предотвратит почленную инициализацию всюду, кроме функций-членов и друзей класса.

2. Запретить почленную инициализацию в функциях-членах и друзьях класса, намеренно не предоставляя определения копирующего конструктора (однако объявить его так, как описано на шаге 1, все равно нужно). Язык не дает нам возможности ограничить доступ к закрытым членам класса со стороны функций-членов и друзей. Но если определение отсутствует, то любая попытка вызвать копирующий конструктор, законная с точки зрения компилятора, приведет к ошибке во время редактирования связей, поскольку не удастся найти определение символа.

class Account { public:

Account();

Account( const char*, double=0.0 );

...

private:

Account( const Accounts );

...

Чтобы запретить почленную инициализацию, класс Account можно объявить так:

14.6.1. Инициализация члена, являющегося объектом класса

Что произойдет, если в объявлении name заменить С-строку на тип класса string? Как это повлияет на почленную инициализацию по умолчанию? Как надо будет изменить явный копирующий конструктор? Мы ответим на эти вопросы в данном подразделе.

При почленной инициализации по умолчанию исследуется каждый член. Если он принадлежит к встроенному или составному типу, то такая инициализация применяется непосредственно. Например, в первоначальном определении класса Account член name инициализируется непосредственно, так как это указатель:

newAcct. name = oldAcct. name;



inline Account:: Account( const Account srhs ) {

acct nmr = rhs. acct nmbr; balance = rhs. balance;

Псевдокод на C++

иллюстрирует вызов копирующего конструктора для члена, являющегося объектом класса name.string::string( rhs. name );

класса Account выглядит следующим образом (хотя явно он не определен):

Теперь почленная инициализация по умолчанию для класса Account корректно обрабатывает выделение и освобождение памяти для name, но все еще неверно копирует номер счета, поэтому приходится кодировать явный копирующий конструктор. Однако

не

Account::

Account( const Account srhs )

совсем правильно...

inline

name = rhs. name; balance = rhs. balance;

acct nmr = get unie acct nmbr() ;

приведенный ниже фрагмент не совсем правилен. Можете ли вы сказать, почему?

Эта реализация ошибочна, поскольку в ней не различаются инициализация и присваивание. В результате вместо вызова копирующего конструктора string мы вызываем конструктор string по умолчанию на фазе неявной инициализации и копирующий оператор присваивания string - в теле конструктора. Исправить это несложно:

Члены, являющиеся объектами классов, обрабатываются по-другому. В инструкции

Account newAcct( oldAcct );

оба объекта распознаются как экземпляры Account. Если у этого класса есть явный копирующий конструктор, то он и применяется для задания начального значения, в противном случае выполняется почленная инициализация по умолчанию.

Таким образом, если обнаруживается член-объект класса, то описанный выше процесс применяется рекурсивно. У класса есть явный копирующий конструктор? Если да, вызвать его для задания начального значения члена-объекта класса. Иначе применить к этому члену почленную инициализацию по умолчанию. Если все члены этого класса принадлежат к встроенным или составным типам, то каждый инициализируется непосредственно и процесс на этом завершается. Если же некоторые члены сами являются объектами классов, то алгоритм применяется к ним рекурсивно, пока не останется ничего, кроме встроенных и составных типов.

В нашем примере у класса string есть явный копирующий конструктор, поэтому name инициализируется с помощью его вызова. Копирующий конструктор по умолчанию для



inline Account:: Account( const Account srhs ) : name ( rhs. name )

balance = rhs. balance; acct nmr = get unie acct nmbr() ;

Самое главное - понять, что такое исправление необходимо. (Обе реализации приводят к тому, что в name копируется значение из rhs. name, но в первой одна и та же работа выполняется дважды.) Общее эвристическое правило состоит в том, чтобы по возможности инициализировать все члены-объекты классов в списке инициализации членов.

Упражнение 14.13

Для какого определения класса скорее всего понадобится копирующий конструктор?

1. Представление Point3w, содержащее чет1ре числа с плавающей точкой.

2. Класс miatrix, в котором память для хранения матрица: выделяется динамически в конструкторе и освобождается в деструкторе.

3. Класс payroll (платежная ведомость), где каждому объекту приписывается уникальный идентификатор.

4. Класс word (слово), содержащий объект класса string и вектор, в котором хранятся пары (номер строки, смещение в строке).

Упражнение 14.14

Реализуйте для каждого из данных классов копирующий конструктор, конструктор по

(a) class BinStrTreeNode { public:

...

private:

string value; int count;

Di таСФааТ\Та,а

BinStrTreeNode * leftchild; BinStrTreeNode * rightchild;

умолчанию и деструктор.

(b) class BinStrTree { public: ...

private:

BinStrTreeNode * root;



1 ... 228 229 230 [ 231 ] 232 233 234 ... 395

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика