ci = ii; // ошибка: нельзя присваивать константному члену
ri = i; // ошибка: ri не инициализирована
}
К началу выполнения тела конструктора инициализация всех константных членов и членов-ссылок должна быть завершена. Для этого нужно указать их в списке инициализации. Правильная реализация предыдущего примера такова:
// правильно: инициализируются константные члены и ссылки
ConstRef::
ConstRef( int ii )
: ci( ii ), ri ( i )
{ i = ii; }
Каждый член должен встречаться в списке инициализации не более одного раза. Порядок инициализации определяется не порядком следования имен в списке, а порядком объявления членов. Если дано следующее объявление членов класса Account:
class Account {
public:
// ...
private:
unsigned int _acct_nmbr;
double _balance;
string _name;
};
то порядок инициализации для такой реализации конструктора по умолчанию
inline Account::
Account() : _name( string() ), _balance( 0.0 ), _acct_nmbr( 0 )
{}
будет следующим: _acct_nmbr, _balance, _name. Однако члены, указанные в списке (или в неявно инициализируемом члене-объекте класса), всегда инициализируются раньше, чем производится присваивание членам в теле конструктора. Например, в следующем конструкторе:
inline Account::
Account( const char* name, double bal )
: _name( name ), _balance( bal )
{
_acct_nmbr = get_unique_acct_nmbr();
}
порядок инициализации такой: _balance, _name, _acct_nmbr.
Расхождение между порядком инициализации и порядком следования членов в соответствующем списке может приводить к трудным для обнаружения ошибкам, когда один член класса используется для инициализации другого:
class X {
int i;
int j;
public:
// видите проблему?
X( int val )
: j( val ), i( j )
{}
// ...
};
кажется, что перед использованием для инициализации i член j уже инициализирован значением val, но на самом деле i инициализируется первым, для чего применяется еще неинициализированный член j. Мы рекомендуем помещать инициализацию одного члена другим (если вы считаете это необходимым) в тело конструктора:
// предпочтительная идиома
X::X( int val ) : i( val ) { j = i; }
Упражнение 14.12
Что неверно в следующих определениях конструкторов? Как бы вы исправили обнаруженные ошибки?
(a) Word::Word( char *ps, int count = 1 )
: _ps( new char[strlen(ps)+1] ),
_count( count )
{
if ( ps )
strcpy( _ps, ps );
else {
_ps = 0;
_count = 0;
}
}
(b) class CL1 {
public:
CL1() { c.real(0.0); c.imag(0.0); s = " not set" ; }
// ...
private:
complex c;
string s;
}
(c) class CL2 {
public:
CL2( mapstring,location *pmap, string key )
: _text( key ), _loc( (*pmap)[key] ) {}
// ...
private:
location _loc;
string _text;
};
14.6. Почленная инициализация A
Инициализация одного объекта класса другим объектом того же класса, как, например:
Account oldAcct( " Anna Livia Plurabelle" );
Account newAcct( oldAcct );
называется
Такую инициализацию проще всего представить, если считать, что компилятор создает специальный внутренний копирующий конструктор, где поочередно, в порядке объявления, инициализируются все нестатические члены. Если рассмотреть первое определение нашего класса Account:
class Account {
public:
// ...
private:
char *_name;
unsigned int _acct_nmbr;
double _balance;
};
то можно представить, что копирующий конструктор по умолчанию определен так:
inline Account::
Account( const Account &rhs )
{
_name = rhs._name;
_acct_nmbr = rhs._acct_nmbr;