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

  • Для улучшения производительности за счет уменьшения издержек на вызов функции вы можете заставить компилятор C++ встроить в программу код функции, подобно тому, как это делается при замещении макрокоманд.
  • Используя встроенные (inline) функции, ваши программы остаются удобными для чтения (читающий программу видит вызов функции), но вы избегаете издержек на вызов функции, которые вызваны помещением параметров в стек и их последующим извлечением из стека, а также переходом к телу функции и последующим возвратом из нее.
  • В зависимости от требований, предъявляемых к вашей программе, иногда вам потребуется использовать язык ассемблера для решения определенной задачи.
  • Для упрощения применения программирования на языке ассемблера C++ позволяет определить функции на встроенном языке ассемблера внутри ваших программ на C++.

ВСТРОЕННЫЕ ФУНКЦИИ

Когда вы определяете в своей программе функцию, компилятор C++ переводит код функции в машинный язык, сохраняя только одну копию инструкций функции внутри вашей программы. Каждый раз, когда ваша программа вызывает функцию, компилятор C++ помещает в программу специальные инструкции, которые заносят параметры функции в стек и затем выполняют переход к инструкциям этой функции. Когда операторы функции завершаются, выполнение программы продолжается с первого оператора, который следует за вызовом функции. Помещение аргументов в стек и переход в функцию и из нее вносит издержки, из-за которых ваша программа выполняется немного медленнее, чем если бы она размещала те же операторы прямо внутри программы при каждой ссылке на функцию. Например, предположим, что следующая программа CALLBEEP.CPP вызывает функциюshow_message, которая указанное число раз выдает сигнал на динамик компьютера и затем выводит сообщение на дисплей:

#include

void show_message(int count, char *message)

{
   int i;
   for (i = 0; i < count; i++) cout << 'a';
   cout << message << endl;
}

void main(void)

{
   show_message(3, "Учимся программировать на языке C++");
   show_mes sage(2, "Урок 35");
}

Следующая программа NO_CALL.CPP не вызывает функциюshow_message. Вместо этого она помещает внутри себя те же операторы функции при каждой ссылке на функцию:

#include

void main (void)

{
   int i;
   for (i = 0; i < 3; i++) cout << 'a';
   cout << " Учимся программировать на языке C++" << endl;
   for (i = 0; i < 2; i++) cout << 'a';
   cout << "Урок 35" << endl;
}

Обе программы выполняют одно и то же. Поскольку программа NO_CALL не вызывает функциюshow_message, она выполняется немного быстрее, чемпрограмма CALLBEEP. В данном случае разницу во времени выполнения определить невозможно, но, если в обычной ситуации функция будет вызываться 1000 раз, вы, вероятно, заметите небольшое увеличение производительности. Однако программа NO_CALL более запутана, чем ее двойник CALL_BEEP, следовательно, более тяжела для восприятия.

При создании программ вы всегда должны попытаться определить, когда лучше использовать обычные функции, а когда лучше воспользоваться встроенными функциями. Для более простых программ предпочтительно использовать обычные функции. Однако, если вы создаете программу, для которой производительность имеет первостепенное значение, вам следовало бы уменьшить количество вызовов функций. Один из способов уменьшения количества вызовов функций состоит в том, чтобы поместить соответствующие операторы прямо в программу, как только что было сделано в программе NO_CALL. Однако, как вы могли убедиться, замена только одной функции внесла значительную путаницу в программу. К счастью, C++ предоставляет ключевое словоinline, которое обеспечивает лучший способ.

Использование ключевого слова inline

При объявлении функции внутри программы C++ позволяет вам предварить имя функции ключевым словомinline. Если компилятор C++ встречает ключевое словоinline, он помещает в выполнимый файл (машинный язык) операторы этой функции в месте каждого ее вызова. Таким образом, можно улучшить читаемость ваших программ на C++, используя функции, и в то же время увеличить производительность, избегая издержек на вызов функций. Следующая программа INLINE.CPP определяет функции тах иmin какinline:

#include

inline int max(int a, int b)

{
   if (a > b) return(a);
   else return(b) ;
}

inline int min(int a, int b)

{
   if (a < b) return(a);
   else return(b);
}

void main(void)

{
   cout << "Минимум из 1001 и 2002 равен" << min(1001, 2002) << endl;
   cout << "Максимум из 1001 и 2002 равен" << max(1001, 2002) << endl;
}

В данном случае компилятор C++ заменит каждый вызов функции на соответствующие операторы функции. Производительность программы увеличивается без ее усложнения.

О встроенных функциях

Если компилятор C++ перед определением функции встречает ключевое словоinline, он будет заменять обращения к этой функции (вызовы) на последовательность операторов, эквивалентную выполнению функции. Таким образом ваши программы улучшают производительность, избавляясь от издержек на вызов функции и в то же время выигрывая в стиле программы, благодаря использованию функций.

ВСТРОЕННЫЕ ФУНКЦИИ И КЛАССЫ

Как вы уже знаете, при определении класса вы определяете функции этого класса внутри или вне класса. Например, классemployee определяет свои функции внутри самого класса:

class employee

{
public:
   employee(char *name, char *position, float salary)

   {
      strcpy(employee::name, name);
      strcpy(employee::position, position);
      employee::salary = salary;
   }

   void show_employee(void)

   {
      cout << "Имя:" << name << endl;
      cout << "Должность:" << position << endl;
      cout << "Оклад: $" << salary << endl;
   }

private:
   char name [64];
   char position[64];
   float salary;
};

Размещая подобным образом функции внутри класса, вы тем самым объявляете их встроенными{inline). Если вы создаете встроенные функции класса этим способом, C++ дублирует функцию для каждого создаваемого объекта этого класса, помещая встроенный код при каждой ссылке на метод (функцию) класса. Преимущество такого встроенного кода состоит в увеличении производительности. Недостатком является очень быстрое увеличение объема самого определения класса. Кроме того, включение кода функции в определение класса может существенно запугать класс, делая его элементы трудными для восприятия.

Для улучшения читаемости определений ваших классов вы можете вынести функции из определения класса, как вы обычно и делаете, и разместить ключевое словоinline перед определением функции. Например, следующее определение заставляет компилятор использовать встроенные операторы для функцииshow_employee:

inline void employee::show_employee(void)

{
   cout << "Имя: " << name << endl;
   cout << "Должность:" << position << endl;
   cout << "Оклад: $" << salary << endl;
}

ИСПОЛЬЗОВАНИЕ ОПЕРАТОРОВ ЯЗЫКА АССЕМБЛЕРА

Как вы знаете из урока 1, программисты могут создавать программы, используя широкий спектр языков программирования. Затем компилятор преобразует операторы программы в машинный код (нули и единицы), который понимает компьютер. Каждый тип компьютеров поддерживает промежуточный язык, называемый языком ассемблера, который попадает в категорию между машинным языком и языком программирования, таким как C++.

Язык ассемблера использует другие символы для представления инструкций машинного языка. В зависимости от назначения ваших программ, возможно, вам потребуется выполнить операции низкого уровня, для которых необходимо использовать операторы языка ассемблера. В таких случаях вы можете использовать оператор C++ asm для встраивания операторов языка ассемблера в программу. Большинство создаваемых вами программ не потребуют операторов языка ассемблера. Следующая программа USE_ASM.CPP использует операторasm, чтобы вставить операторы языка ассемблера, необходимые для озвучивания динамика компьютера в среде MS-DOS:

#include

void main(void)

{
   cout << "Сейчас будет звонить!"<< endl;
   asm

   {
      MOV AH,2
      MOV DL,7
      INT 21H
   }

   cout << "Есть!"<< endl;
}

Как видите, используя операторasm, программа комбинирует C++ и операторы языка ассемблера.

ЧТО ВАМ НЕОБХОДИМО ЗНАТЬ

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

    1. Помещение параметров в стек и переход к функции и из нее вносит издержки, из-за которых ваша программа выполняется немного медленнее.
    2. Ключевое словоinline заставляет компилятор C++ заменять вызов функции эквивалентной последовательностью операторов, которые бы выполняла эта функция. Поскольку встроенные операторы избавляют от издержек на вызов функции, программа будет выполняться быстрее.
    3. Если вы используете встроенные функции внутри класса, каждый создаваемый вами объект использует свои собственные встроенные операторы. Обычно все объекты одного и того же класса совместно используют один и тот же код функции.
    4. Ключевое словоasm позволяет вам встраивать операторы языка ассемблера в программы на C++.