Выбрать главу

Account( const char *name, double open_balance );

Объект класса Account, инициализируемый конструктором, можно объявить следующим образом:

Account newAcct( "Mikey Matz", 0 );

Если же есть много счетов, для которых начальный баланс равен 0, то полезно иметь конструктор, задающий только имя владельца и автоматически инициализирующий _balance нулем. Один из способов сделать это - предоставить конструктор вида:

Account( const char *name );

Другой способ - включить в конструктор с двумя параметрами значение по умолчанию, равное нулю:

Account( const char *name, double open_balance = 0.0 );

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

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

class Account {

public:

// конструктор по умолчанию ...

Account();

// имена параметров в объявлении указывать необязательно

Account( const char*, double=0.0 );

const char* name() { return name; }

// ...

private:

// ...

};

Ниже приведены два примера правильного определения объекта класса Account, где конструктору передается один или два аргумента:

int main()

{

// правильно: в обоих случаях вызывается конструктор

// с двумя параметрами

Account acct( "Ethan Stern" );

Account *pact = new Account( "Michael Lieberman", 5000 );

if ( strcmp( acct.name(), pact-name() ))

// ...

}

C++ требует, чтобы конструктор применялся к определенному объекту до его первого использования. Это означает, что как для acct, так и для объекта, на который указывает pact, конструктор будет вызван перед проверкой в инструкции if.

Компилятор перестраивает нашу программу, вставляя вызовы конструкторов. Вот как, по всей вероятности, будет модифицировано определение acct внутри main():

// псевдокод на C++,

// иллюстрирующий внутреннюю вставку конструктора

int main()

{

Account acct;

acct.Account::Account("Ethan Stern", 0.0);

// ...

}

Конечно, если конструктор определен как встроенный, то он подставляется в точке вызова.

Обработка оператора new несколько сложнее. Конструктор вызывается только тогда, когда он успешно выделил память. Модификация определения pact в несколько упрощенном виде выглядит так:

// псевдокод на C++,

// иллюстрирующий внутреннюю вставку конструктора при обработке new

int main()

{

// ...

Account *pact;

try {

pact = _new( sizeof( Account ));

pact-Acct.Account::Account(

"Michael Liebarman", 5000.0);

}

catch( std::bad_alloc ) {

// оператор new закончился неудачей:

// конструктор не вызывается

}

// ...

}

Существует три в общем случае эквивалентных формы задания аргументов конструктора:

// в общем случае эти формы эквивалентны

Account acct1( "Anna Press" );

Account acct2 = Account( "Anna Press" );

Account acct3 = "Anna Press";

Форма acct3 может использоваться только при задании единственного аргумента. Если аргументов два или более, мы рекомендуем пользоваться формой acct1, хотя допустима и acct2.

// рекомендуемая форма вызова конструктора

Account acct1( "Anna Press" );

Новички часто допускают ошибку при объявлении объекта, инициализированного конструктором по умолчанию:

// увы! работает не так, как ожидалось

Account newAccount();

Эта инструкция компилируется без ошибок. Однако при попытке использовать объект в таком контексте: