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

1 ... 225 226 227 [ 228 ] 229 230 231 ... 395


string new client( Steve Hall );

не может инициализировать член типа string. Например:

Account new acct( new client, 25000 );

не будет компилироваться, так как не существует неявного преобразования из типа string в тип char*. Инструкция

Account new acct( new client.c str(), 25000 );

правильна, но вызовет у пользователей класса недоумение. Одно из решений - добавить новый конструктор вида:

Account( string, double = 0.0 ); Если написать:

Account new acct( new client, 25000 );

Account *open new account( const char *nm ) {

Account *pact = new Account( nm );

...

return pacct;

вызывается именно этот конструктор, тогда как старый код

по-прежнему будет приводить к вызову исходного конструктора с двумя параметрами.

Так как в классе string определено преобразование из типа char* в тип string (преобразования классов обсуждаются в этой главе ниже), то можно заменить исходный конструктор на новый, которому в качестве первого параметра передается тип string. В таком случае, когда встречается инструкция:

Account myAcct( Tinkerbell );

Tinkerbell преобразуется во временный объект типа string. Затем этот объект передается новому конструктору с двумя параметрами.

При проектировании приходится идти на компромисс между увеличением числа конструкторов класса Account и несколько менее эффективной обработкой аргументов

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

Исходный конструктор Account с двумя параметрами

Account( const char*, double = 0.0 );



#include <string>

class Account { public:

Account();

Account( const char*, double=0.0 );

Account( const strings, double=0.0 ); Account( const Accounts );

private:

...

конструкторов Account будет таким: };

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

1. где вызывается конструктор по умолчанию? Внутри конструктора по умолчанию класса Account;

2. где вызывается копирующий конструктор? Внутри копирующего конструктора класса Account и внутри конструктора с двумя параметрами, принимающего в качестве первого тип string;

3. как передать аргументы конструктору класса, являющегося членом другого класса? Это необходимо делать внутри конструктора Account с двумя параметрами, принимающего в качестве первого тип char*.

Решение заключается в использовании списка инициализации членов (mi упоминали о нем в разделе 14.2). Члены, являющиеся классами, можно явно инициализировать с помощью списка, состоящего из разделенных запят1ми пар имя члена/значение . Наш конструктор с двумя параметрами теперь выглядит так (напомним, что name - это член,

inline Account::

Account( const char* name, double opening bal ) : name( name ), balance( opening bal )

acct nmr = het unie acct nmbr();

являющийся объектом класса string):

Список инициализации членов следует за сигнатурой конструктора и отделяется от нее двоеточием. В нем указывается имя члена, а в скобках - начальные значения, что аналогично синтаксису вызова функции. Если член является объектом класса, то эти значения становятся аргументами, передаваемыми подходящему конструктору, который затем и используется. В нашем примере значение name передается конструктору string, который применяется к члену name. Член balance инициализируется значением opening bal.

Аналогично выглядит второй конструктор с двумя параметрами:

типа char* из-за необходимости создавать временный объект. Мы предоставили две версии конструктора с двумя параметрами. Тогда модифицированный набор



inline Account::

Account( const strings name, double opening bal ) : name( name ), balance( opening bal )

acct nmr = het unie acct nmbr();

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

Часто у новичков возникает вопрос: в чем разница между использованием списка инициализации и присваиванием значений членам в теле конструктора? Например, в чем

inline Account::

Account( const char* name, double opening bal : name( name ), balance( opening bal

acct nmr = het unie acct nmbr(); разница между

Account( const char* name, double opening bal ) {

name = name; balance = opening bal; acct nmr = het unie acct nmbr();

В конце работы обоих конструкторов все три члена будут иметь одинаковые значения. Разница в том, что только список обеспечивает инициализацию тех членов, которые являются объектами класса. В теле конструктора установка значения члена - это не инициализация, а присваивание. Важно это различие или нет, зависит от природы члена.

С концептуальной точки зрения выполнение конструктора состоит из двух фаз: фаза явной или неявной инициализации и фаза вычислений, включающая все инструкции в теле конструктора. Любая установка значений членов во второй фазе рассматривается как присваивание, а не инициализация. Непонимание этого различия приводит к ошибкам и неэффективным программам.

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



1 ... 225 226 227 [ 228 ] 229 230 231 ... 395

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