|
inline-функции
Практический курс по C/C++: https://stepik.org/course/193691
Продолжаем тему
функций языка С++ и следующее нововведение, что мы рассмотрим, касается inline-функций
(«подставляемые» функции). Что это такое? Давайте представим, что мы с вами в
программе объявили функцию сложения двух целочисленных переменных с ее последующим
вызовом:
#include <iostream>
using std::cout;
using std::cin;
using std::endl;
int sum2(int a, int b)
{
return a + b;
}
int main()
{
int x = 1, y = -5;
int res = sum2(x, y);
return 0;
}
Вспоминая все,
что мы с вами говорили о вызовах функций, понимаем всю неэффективность
полученной программы. Действительно, чтобы сложить две переменные между собой
необходимо в стек функции поместить два значения, затем перейти и выполнить
само тело функции, наконец, возвратить результат. И все ради суммы двух чисел,
которую на уровне машинных кодов можно было бы записать двумя-тремя командами! Однако
не все так драматично, как может показаться на первый взгляд. Современные
компиляторы языков Си и С++ довольно умело оптимизируют программу и на уровне
машинного кода вместо вызова таких функций они сразу подставляют их реализации
(тела). Поэтому, просматривая машинный код нашего примера, мы бы не увидели
вызова функции sum2(), а сразу бы обнаружили сложение переменных x и y и присвоения
результата переменной res. Подобный подход компилятор пытается
применить для всех простых в реализации функций. Часто это приводит к
некоторому повышению производительности программы в целом. Однако если такая
подстановка невозможна (например, для рекурсивных функций) или тело функции
содержит довольно большой объем операторов, то компилятор формирует их обычный
вызов с использованием стекового фрейма. Тем не менее, если программист
понимает, что тело функции было бы разумно подставлять в местах ее формального
вызова, то он может сообщить об этом компилятору, прописав перед заголовком
функции ключевое слово inline следующим образом:
#include <math.h>
inline bool sqr_root(double a, double b, double c, double& x1, double& x2)
{
double D = b*b - 4*a*c;
if(D < 0)
return false;
x1 = -(b + sqrt(D)) / (2*a);
x2 = -(b - sqrt(D)) / (2*a);
return true;
}
int main()
{
int x = 1, y = -5;
int res = sum2(x, y);
double a = 0.0, b = 0.0;
bool res_root = sqr_root(3, 10, 5, a, b);
if(res_root)
cout << a << " " << b << endl;
return 0;
}
В теле этой
функции прописано множество операторов и, скорее всего, изначально компилятор
будет создавать ее стандартный вызов. Но благодаря наличию ключевого слова inline, возможно, ее тело
просто будет подставлено в место формального вызова. Хотя, это не
гарантируется. Компилятор сам примет решение, исходя из формируемой программы
на уровне машинных кодов. Тем не менее, ключевое слово inline может сыграть
здесь значимую роль в выборе компилятором способа выполнения этой функции.
На самом деле,
довольно часто функции, прописанные в заголовочных файлах, или внутри структур
и классов, рассматриваются, как inline-функции, так как в заголовках,
обычно, размещаются или прототипы функций или функции с короткими реализациями,
а значит, они вполне могут быть преобразованы в inline-функции. То же
самое и функции внутри классов и структур. Если их реализации прописаны
непосредственно там, то это повод для компилятора рассматривать их как inline. И часто они
именно так переводятся в машинный код, то есть, без реального вызова.
Практический курс по C/C++: https://stepik.org/course/193691
|