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

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

Жанры

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

Майерс Скотт

Шрифт:

Все же давайте не будем слишком увлекаться. В программировании, как и в реальной жизни, не бывает «бесплатных завтраков», и встроенные функции – не исключение. Идея их использования состоит в замене каждого вызова такой функции ее телом. Не нужно быть доктором математических наук, чтобы заметить, что это увеличит общий размер вашего объектного кода. Слишком частое применение встроенных функций на машинах с ограниченной памятью может привести к созданию программы, которая превосходит доступную память. Даже при наличии виртуальной памяти «разбухание» кода, вызванное применением встроенных функций, может привести к дополнительному обмену с диском, уменьшить коэффициент попадания команд в кэш и, следовательно,

снизить производительность программы.

С другой стороны, если тело встроенной функции очень короткое, то сгенерированный для нее код может быть короче кода, сгенерированного для вызова функции. В таком случае встраивание функции может привести к уменьшению объектного кода и повышению коэффициента попаданий в кэш!

Имейте в виду, что директива inline – это совет, а не команда компилятору. Совет может быть сформулирован явно или неявно. Неявный способ заключается в определении встроенной функции внутри определения класса:

class Person {

public:

...

int age const { return theAge;} // неявный запрос на встраивание;

... // функция age определена внутри класса

private:

int theAge;

};

Такие функции обычно являются функциями-членами, но в правиле 46 объясняется, что функции-друзья тоже могут быть определены внутри класса. В этом случае они также неявно считаются встроенными.

Явно объявить встроенную функцию можно, предварив ее определение ключевым словом inline. Например, вот как обычно реализован стандартный шаблон max (из заголовочного файла <algorithm>):

template <typename T> // явный запрос на

inline const T& std::max(const T& a, const T& b) // встраивание: функции

{ return a < b ? b : c;} // std::max предшествует

// слово inline

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

Встроенные функции обычно должны находиться в заголовочных файлах, поскольку большинство разработки программ выполняют встраивание во время компиляции. Чтобы заменить вызовы функции встраиванием ее тела, компилятор должен увидеть эту функцию. (Некоторые среды могут встраивать функции во время компоновки, а есть и такие – например, среды разработки на базе. NET Common Language Infrastructure (CLI), – которые осуществляют встраивание во время исполнения. Но это скорее исключение, чем правило. Встраивание функций в большинстве программ на C++ происходит во время компиляции.)

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

Конкретизация шаблонов никак не связана со встраиванием. Если вы полагаете, что все функции, конкретизированные из вашего шаблона,

должны быть встроенными, объявите шаблон встроенным (inline); именно так разработчики стандартной библиотеки поступили с шаблоном std::max (см. пример выше). Но если вы пишете шаблон для функции, которую нет смысла делать встроенной, не объявляйте встроенным и ее шаблон (явно или неявно). Встраивание обходится дорого, и вряд ли вы захотите платить за это без должного размышления. Мы уже упоминали, что встраивание раздувает код (особенно это важно при разработке шаблонов – см. правило 44), но есть и другие затраты, которые мы скоро обсудим.

Но прежде напомним, что встраивание – это совет, который компилятор может проигнорировать. Большинство компиляторов отвергают встраивание функций, которые представляются слишком сложными (например, содержат циклы или рекурсию), и за исключением наиболее тривиальных случаев, вызов виртуальной функции отменяет встраивание. В этом нет ничего удивительного: virtual означает «какую точно функцию вызвать, определяется в момент исполнения», а inline – «перед исполнением заменить вызов функции ее кодом». Если компилятор не знает, какую функцию вызывать, то трудно винить его в том, что он отказывается делать встраивание.

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

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

inline void f {...} // предположим, что компилятор может встроить вызовы f

void (*pf) = f; // pf указывает на f

...

f; // этот вызов будет встроенным, потому что он

// «нормальный»

pf; // этот вызов, вероятно, не будет встроен, потому что

// функция вызвана по указателю

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

Фактически конструкторы и деструкторы часто являются наихудшими кандидатами для встраивания. Например, рассмотрим конструктор класса Derived:

class Base {

public:

...

private:

std::string bm1, bm2; // члены базового класса 1 и 2

};

class Derived: public Base {

public:

Derived{} // конструктор Derived пуст – не так ли?

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

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

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

Помещица Бедная Лиза

Шах Ольга
Любовные романы:
любовно-фантастические романы
6.40
рейтинг книги
Помещица Бедная Лиза

Бальмануг. Студентка

Лашина Полина
2. Мир Десяти
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Бальмануг. Студентка

Изменить нельзя простить

Томченко Анна
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Изменить нельзя простить

Газлайтер. Том 3

Володин Григорий
3. История Телепата
Фантастика:
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
Газлайтер. Том 3

Жандарм 4

Семин Никита
4. Жандарм
Фантастика:
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
Жандарм 4

Кодекс Охотника. Книга X

Винокуров Юрий
10. Кодекс Охотника
Фантастика:
фэнтези
попаданцы
аниме
6.25
рейтинг книги
Кодекс Охотника. Книга X

Отмороженный 6.0

Гарцевич Евгений Александрович
6. Отмороженный
Фантастика:
боевая фантастика
постапокалипсис
рпг
5.00
рейтинг книги
Отмороженный 6.0

Пипец Котенку!

Майерс Александр
1. РОС: Пипец Котенку!
Фантастика:
фэнтези
юмористическое фэнтези
аниме
5.00
рейтинг книги
Пипец Котенку!

АН (цикл 11 книг)

Тарс Элиан
Аномальный наследник
Фантастика:
фэнтези
героическая фантастика
попаданцы
аниме
5.00
рейтинг книги
АН (цикл 11 книг)

Толян и его команда

Иванов Дмитрий
6. Девяностые
Фантастика:
попаданцы
альтернативная история
7.17
рейтинг книги
Толян и его команда

Пятое правило дворянина

Герда Александр
5. Истинный дворянин
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Пятое правило дворянина

Игрок, забравшийся на вершину. Том 8

Михалек Дмитрий Владимирович
8. Игрок, забравшийся на вершину
Фантастика:
фэнтези
рпг
5.00
рейтинг книги
Игрок, забравшийся на вершину. Том 8

Возвышение Меркурия. Книга 7

Кронос Александр
7. Меркурий
Фантастика:
героическая фантастика
попаданцы
аниме
5.00
рейтинг книги
Возвышение Меркурия. Книга 7