|
Программирование >> Инициализация объектов класса, структура
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;
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |