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();
Эта инструкция компилируется без ошибок. Однако при попытке использовать объект в таком контексте: