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

Видео по теме