Чтение онлайн

на главную - закладки

Жанры

Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ

Майерс Скотт

Шрифт:

public:

ABEntry(const std::string& name, const std::string& address,

const std::list<PhoneNumber>& phones);

private:

std::string theName;

std::string theAddress;

std::list<PhoneNumber> thePhones;

int numTimesConsulted;

};

ABEntry(const std::string& name, const std::string& address,

const std::list<PhoneNumber>& phones)

{

theName = name; //
все это присваивание, а не инициализация

theAddress = address;

thePhones = phones;

numTimesConsulted = 0;

}

Да, в результате порождаются объекты ABEntry со значениями, которых вы ожидаете, но это все же не лучший подход. Правила C++ оговаривают, что члены объекта инициируются перед входом в тело конструктора. То есть внутри конструктора ABEntry члены theName, theAddress и thePhones не инициализируются, а им присваиваются значения. Инициализация происходит ранее: когда автоматически вызываются их конструкторы перед входом в тело конструктора ABEntry. Это не касается numTimesConsulted, поскольку этот член относится к встроенному типу. Для него нет никаких гарантий того, что он вообще будет инициализирован перед присваиванием.

Лучший способ написания конструктора ABEntry – использовать список инициализации членов вместо присваивания:

ABEntry(const std::string& name, const std::string& address,

const std::list<PhoneNumber>& phones)

:theName(name), // теперь это все – инициализации

:theAddress(address),

thePhones(phones),

:numTimesConsulted(0)

{} // тело конструктора теперь пусто

Этот конструктор дает тот же самый конечный результат, что и предыдущий, но часто оказывается более эффективным. Версия, основанная на присваиваниях, сначала вызывает конструкторы по умолчанию для инициализации theName, theAddress и thePhones, а затем сразу присваивает им новые значения, затирая те, что уже были присвоены в конструкторах по умолчанию. Таким образом, вся работа конструкторов по умолчанию тратится впустую. Подход со списком инициализации членов позволяет избежать этой проблемы, поскольку аргументы в списке инициализации используются в качестве аргументов конструкторов для различных членов-данных. В этом случае theName создается конструктором копирования из name, theAddress – из address, thePhones – из phones. Для большинства типов единственный вызов конструктора копирования более эффективен – иногда намного более эффективен, чем вызов конструкторов по умолчанию с последующим вызовом операторов присваивания.

Для объектов встроенных типов вроде numTimesConsulted нет разницы по затратам между инициализацией и присваиванием, но для единообразия часто лучше инициировать все посредством списка инициализации членов. Такие списки можно применять даже тогда, когда данные-члены инициализируются конструкторами по умолчанию: просто не передавайте никаких аргументов соответствующему конструктору. Например, если у ABEntry есть конструктор, не принимающий параметров, то он может быть реализован примерно так:

ABEntry

:theName, // вызвать конструктор по умолчанию для theName

:theAddress, // сделать то же для theAddress и для thePhones;

thePhones, // но явно инициализировать нулем numTimesConsulted

:numTimesConsulted(0)

{}

Поскольку

компилятор автоматически вызывает конструкторы по умолчанию для данных-членов пользовательских типов, когда для них отсутствуют инициализаторы в списке инициализации членов, некоторые программисты считают приведенный выше код избыточным. Это понятно, но, придерживаясь политики всегда перечислять все данные-члены в списках инициализации, вы избавляете себя от необходимости помнить, какие члены будут инициализированы, если их пропустить, а какие – нет. Например, поскольку numTimesConsulted относится к встроенному типу, то исключение его из списка инициализации может открыть двери неопределенному поведению.

Иногда список инициализации просто необходимо использовать, даже для встроенных типов. Например, данные-члены, которые являются константами либо ссылками, обязаны быть инициализированы, так как они не могут получить значения посредством присваивания (см. также правило 5). Чтобы избежать необходимости помнить, когда данные-члены должны быть инициализированы в списке инициализации, а когда это не обязательно, проще делать это всегда. Иногда это обязательно, а часто – более эффективно, чем присваивание.

Во многих классах есть несколько конструкторов, и каждый конструктор имеет свой собственный список инициализации. Если у класса много данных-членов или базовых классов, то наличие большого числа списков инициализации порождает нежелательное дублирование кода (в списках) и тоску (у программистов). В таких случаях имеет смысл опустить в списках инициализации те данные-члены, для которых присваивание работает так же, как настоящая инициализация, переместив инициализацию в одну (обычно закрытую) функцию, которую вызывают все конструкторы. Этот подход может быть особенно полезен, если начальные значения должны быть загружены из файла или базы данных. Однако, вообще говоря, инициализация членов посредством списков инициализации более предпочтительна, чем псевдоинициализация присваиванием.

Один из аспектов C++, на который можно положиться, – это порядок, в котором инициализируются данные объектов. Этот порядок всегда один и тот же: базовые классы инициализируются раньше производных (см. также правило 12), а внутри класса члены-данные инициализируются в том порядке, в котором объявлены. Например, в классе ABEntry член theName всегда будет инициализирован первым, theAddress – вторым, thePhones – третьим, а numTimesConsulted – последним. Это верно даже в случае, если в списке инициализации членов они перечислены в другом порядке (что, к сожалению, не запрещено). Чтобы не вводить в заблуждение человека, читающего вашу программу, и во избежание ошибок непонятного происхождения, всегда перечисляйте данные-члены в списке инициализации в том порядке, в котором они объявлены в классе.

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

Отнесемся к этой фразе со всем вниманием.

Статический объект существует от момента, когда был сконструирован, и до конца работы программы. Объекты, размещенные в стеке и в «куче», к статическим не относятся. Статическими являются глобальные объекты, объекты, объявленные в области действия пространства имен, объекты, объявленные с ключевым словом static внутри классов и функций, а также в области действия отдельного файла с исходным текстом. Статические объекты, объявленные внутри функций, известны как локальные статические объекты (поскольку они локальны по отношению к функции), а все прочие называют нелокальными статическими объектами. Статические объекты автоматически уничтожаются при завершении программы, то есть при выходе из функции main автоматически вызываются их деструкторы.

Поделиться:
Популярные книги

Метатель

Тарасов Ник
1. Метатель
Фантастика:
боевая фантастика
попаданцы
рпг
фэнтези
фантастика: прочее
постапокалипсис
5.00
рейтинг книги
Метатель

Магия чистых душ

Шах Ольга
Любовные романы:
любовно-фантастические романы
5.40
рейтинг книги
Магия чистых душ

Барон Дубов

Карелин Сергей Витальевич
1. Его Дубейшество
Фантастика:
юмористическое фэнтези
аниме
сказочная фантастика
фэнтези
5.00
рейтинг книги
Барон Дубов

#Бояръ-Аниме. Газлайтер. Том 11

Володин Григорий Григорьевич
11. История Телепата
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
#Бояръ-Аниме. Газлайтер. Том 11

Барон диктует правила

Ренгач Евгений
4. Закон сильного
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Барон диктует правила

Попытка возврата. Тетралогия

Конюшевский Владислав Николаевич
Попытка возврата
Фантастика:
альтернативная история
9.26
рейтинг книги
Попытка возврата. Тетралогия

Долгий путь домой

Русич Антон
Вселенная EVE Online
Фантастика:
космическая фантастика
попаданцы
6.20
рейтинг книги
Долгий путь домой

Гардемарин Ее Величества. Инкарнация

Уленгов Юрий
1. Гардемарин ее величества
Фантастика:
городское фэнтези
попаданцы
альтернативная история
аниме
фантастика: прочее
5.00
рейтинг книги
Гардемарин Ее Величества. Инкарнация

Завод-3: назад в СССР

Гуров Валерий Александрович
3. Завод
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Завод-3: назад в СССР

Решала

Иванов Дмитрий
10. Девяностые
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Решала

Камень. Книга пятая

Минин Станислав
5. Камень
Фантастика:
боевая фантастика
6.43
рейтинг книги
Камень. Книга пятая

Отдельный танковый

Берг Александр Анатольевич
1. Антиблицкриг
Фантастика:
боевая фантастика
альтернативная история
5.00
рейтинг книги
Отдельный танковый

Метатель. Книга 3

Тарасов Ник
3. Метатель
Фантастика:
попаданцы
альтернативная история
рпг
фэнтези
фантастика: прочее
постапокалипсис
5.00
рейтинг книги
Метатель. Книга 3

Боярышня Евдокия

Меллер Юлия Викторовна
3. Боярышня
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Боярышня Евдокия