Сформулируем правила, которые описывают, когда инициализация объекта гарантируется, а когда нет. К сожалению, эти правила достаточно сложны – на мой взгляд, слишком сложны, чтобы их стоило запоминать. Вообще, если вы работаете с C-частью C++ (см. правило 1) и инициализация может стоить определенных затрат во время исполнения, то не гарантируется, что она произойдет. Это объясняет, почему содержимое массивов (в C-части C++) не обязательно инициализируется, а содержимое вектора (из STL-части C++) инициализируется всегда.
По-видимому, лучший способ поведения в такой неопределенной ситуации –
int x = 0; // ручная инициализация int
const char * text = “Строка в стиле C”; // ручная инициализация указателя
// (см. также правило 3)
double d; // «инициализация» чтением
std::cin d; // из входного потока
Почти во всех остальных случаях ответственность за инициализацию ложится на конструкторы. Правило простое: убедитесь, что все конструкторы инициализируют в объекте всё.
Этому правилу легко следовать, но важно не путать присваивание с инициализацией. Рассмотрим конструктор класса, представляющего записи в адресной книге:
class PhoneNumber {…}
class ABEntry { // ABEntry = “Address Book Entry”
public:
ABEntry(const std::string name, const std::string address,
const std::listPhoneNumber phones);
private:
std::string theName;
std::string theAddress;
std::listPhoneNumber thePhones;
int numTimesConsulted;
};
ABEntry(const std::string name, const std::string address,
const std::listPhoneNumber phones)
{
theName = name; // все это
theAddress = address;
thePhones = phones;
numTimesConsulted = 0;
}
Да, в результате порождаются объекты ABEntry со значениями, которых вы ожидаете, но это все же не лучший подход. Правила C++ оговаривают, что члены объекта инициируются
Лучший способ написания конструктора ABEntry – использовать список инициализации членов вместо присваивания:
ABEntry(const std::string name, const std::string address,
const std::listPhoneNumber phones)
:theName(name), // теперь это все –
:theAddress(address),
thePhones(phones),
:numTimesConsulted(0)
{} // тело конструктора теперь пусто
Этот конструктор дает тот же самый конечный результат, что и предыдущий, но часто оказывается более эффективным. Версия, основанная на присваиваниях, сначала вызывает конструкторы по умолчанию для инициализации theName, theAddress и thePhones, а затем сразу присваивает им новые значения, затирая те, что уже были присвоены в конструкторах по умолчанию. Таким образом, вся работа конструкторов по умолчанию тратится впустую. Подход со списком инициализации членов позволяет избежать этой проблемы, поскольку аргументы в списке инициализации используются в качестве аргументов конструкторов для различных членов-данных. В этом случае theName создается конструктором копирования из name, theAddress – из address, thePhones – из phones. Для большинства типов единственный вызов конструктора копирования более эффективен – иногда