Г. Шилтд - Самоучитель C++ (PDF) (1114887), страница 12
Текст из файла (страница 12)
Несмотря на то, что эти переменные объявлены как часть анонимногообъединения, их имена находятся в той же области видимости, что и другиеобъявленные здесь локальные переменные. Именно по этой причине членанонимного объединения не может иметь то же имя, что и любая переменная в данной области видимости.1. Перепишите класс stack, представленный в разделе 2.1, чтобы вместо классаиспользовалась структура.2. Используйте объединение, чтобы поменять местами старший и младшийбайты целого (предполагается 16-битное целое; если ваш компьютер использует 32-битное целое, то меняйте местами байты типа short int).3.
Объясните, что такое анонимное объединение и чем оно отличается от нормального объединения.72_СамоучительC++2.6. Встраиваемые функцииПеред тем как продолжить исследование классов необходимо краткое отступление. В C++ можно задать функцию, которая на самом деле не вызывается, а ее тело встраивается в программу в месте ее вызова. Она действуетпочти так же, как макроопределение с параметрами в С. Преимуществомвстраиваемых (in-line) функций является то, что они не связаны с механизмом вызова функций и возврата ими своего значения.
Это значит, чтовстраиваемые функции могут выполняться гораздо быстрее обычных.(Запомните, что выполнение машинных команд, которые генерируют вызовфункции и возвращение функцией своего значения, занимает определенноевремя. Если функция имеет параметры, то ее вызов занимает еще большеевремя.)Недостатком встраиваемых функций является то, что если они слишкомбольшие и вызываются слишком часто, объем ваших программ сильно возрастает. Из-за этого применение встраиваемых функций обычно ограничивается короткими функциями.Для объявления встраиваемой функции просто впишите спецификатор inlineперед определением функции.
Например, в этой короткой программе показано, как объявить встраиваемую функцию:// Пример встраиваемой функции^include <iostreara>using namespace std;-inline int even (int x){return ! (x%2);int main (){if (even (10)) cout « "10 является четным\п";if (even (11)) cout « "11 является четным\п";return 0;В этом примере функция even(), которая возвращает истину при четномаргументе, объявлена встраиваемой. Это означает, что строкаif (even(10)) cout « "10 является четным\п";функционально идентична строкеif (! ( 1 0 % 2 ) ) cout « "10 является четнымХп";Этот пример указывает также на другую важную особенность использованиявстраиваемой функции: она должна быть задана до ее первого вызова.
ЕслиГлава 2. Введение в классы73это не так, компилятор не будет знать, какой именно код предполагаетсявстроить в программу с помощью встраиваемой функции. Поэтому функцияeven() была определена перед функцией main().В пользу использования встраиваемых функций вместо макроопределений спараметрами имеется два довода. Во-первых, они обеспечивают болеестройный способ встраивания в программу коротких фрагментов кода. Например, при создании макроса с параметрами легко забыть, что для гарантии правильности встраивания в каждом случае часто требуются круглыевнешние скобки. Встраиваемая функция исключает эту проблему.Во-вторых, компилятор гораздо лучше работает со встраиваемой функцией,чем с макрорасширением. Как правило, программисты C++ для многократных вызовов коротких функций вместо макросов с параметрами практически всегда используют встраиваемые функции.Здесь важно понимать, что спецификатор inline для компилятора являетсязапросом, а не командой.
Если, по разным причинам, компилятор не в состоянии выполнить запрос, функция будет компилироваться, как обычнаяфункция, а запрос inline будет проигнорирован.В зависимости от типа вашего компилятора возможны некоторые ограничения на использование встраиваемых функций. Например, некоторые компиляторы не воспринимают функцию как встраиваемую, если функцияявляется рекурсивной или если она содержит либо статическую (static) переменную, либо любую инструкцию выполнения цикла, либо инструкциюswitch, либо инструкцию goto.
Вам необходимо просмотреть руководство повашему компилятору, чтобы точно определить ограничения на использование встраиваемых функций.Если какое-либо ограничение на использование встраиваемой функции нарушено, компилятор генерирует вместо нее обычную функцию.ПримерыРЛюбая функция может стать встраиваемой, включая функции — члены классов. Например, функция divisible!) для ускорения ее выполнения сделанавстраиваемой. (Функция возвращает истину, если ее первый аргумент без остатка может делиться на второй.)// Демонстрация встраиваемой функции-члена^include <iostream>using namespace std;вL_74СамоучительC++class samp {int i , j ;public:sampfint a, int b) ;int divisible О; // встраивание происходит в этом определенииsamp: : samp {int a, int b)(i = a;j .= b;/* Возврат 1, если i без остатка делится на j.
Тело этой функциичлена встраивается в программу*/inline int samp: :divisible (){return ! (i%j ) ;int rnainOiisamp obi[10, 2), ob2(10, 3);// это истинаif(obi.divisible{)) cout « "10 делится на 2Лп";// это ложьif(ob2.divisible()} cout « "10 делится на 3\п";return 0;2.
Допускается перегружать встраиваемую функцию. Например, эта программаперегружает min() тремя способами. В каждом случае функция также объявляется встраиваемой.tfinclude <iostream>using namespace std;// Перегрузка функции min ( ) тремя способами// intinline int min {int a, int b){return a < b ? a: b;// longinline long min (long a, long b)Глава 2.
Введение в классы75return а < b ? а: Ь;}// doubleinline double rain (double a, double b){return a < b ? a: b;}int main ( ){cout « min(-10, 10) « "\n";cout « min(-10.01, 100.002) « "\ncout « min(-10L, 12L) « "\n";return 0;1. В главе 1 вы перегружали функцию abs() так, чтобы она находила абсолютные значения типа int, long и double. Модифицируйте программу, чтобы этифункции стали встраиваемыми.2. Почему следующая функция может не компилироваться как встраиваемая?void f l ( ){int i;f o r d = 0; i < 10; i++) cout « i;2.7. Встраиваемые функциив объявлении классаЕсли определение функции-члена достаточно короткое, его можно включить в объявление класса. Поступив таким образом, мы заставляем, если этовозможно, функцию стать встраиваемой.
Если функция задается внутриобъявления класса, ключевое слово inline не требуется. (Однако использование его в такой ситуации не является ошибкой.) Например, как показанониже, функция divisible() из предыдущего раздела может быть по умолчаниюсделана встраиваемой:76_______Самоучитель#include <iostream>using namespace std;class samp {int i, j;public:samp (int a, int b) ;/* Функция divisible (), которая здесь определяется, по умолчаниюстановится встраиваемой.Vint divisible () { return !(i%j); }samp: : samp (int a, int b){i = a;j = b;}int main (){samp obi (10, 2), ob2(10, 3);// это истинаif (obi .divisible () ) cout « "10 делится на 2\п";// это ложьif (ob2.
divisible () ) cout « "10 делится на 3\п";return 0;Как видите, код функции divisibleQ находится внутри объявления классаsamp. Отметьте, что никакого другого определения функции divisible{) ненужно, это даже запрещено. Определение функции divisible() внутри классаsamp автоматически заставляет ее стать встраиваемой функцией.Если функция, заданная внутри объявления класса, не может стать встраиваемой функцией (поскольку были нарушены ограничения), она, обычно,преобразуется в обычную функцию.Отметьте, как именно функция divisibleQ задается внутри класса samp, особенно само тело функции. Оно целиком расположено на одной строке. Такой формат для программ C++ является совершенно обычным, еслифункция объявляется внутри объявления класса. Такое объявление становится более компактным.
Однако класс samp мог бы быть описан и так:class samp {int i, j;Глава 2. Введение в классыpublic:samp(int a,77int b);/* Функция divisible(), которая здесь определяется, по умолчаниюстановится встраиваемой.*/int divisible()'return ! ( i % j ) ;}Здесь в определении функции divisibleQ используется более или менее стандартный стиль отступов. С точки зрения компилятора, компактный и стандартный стили не отличаются. Однако в программах C++ при заданиикоротких функций внутри определения класса обычно используется компактный стиль.На применение таких встраиваемых функций накладываются те же ограничения, что и на применение обычных встраиваемых функций.Примеры1.Вероятно наиболее традиционным использованием встраиваемых функций,определяемых внутри класса, является определение конструктора и деструктора.
Например, класс samp может быть определен более эффективно:ttinclude <iostream>using namespace std;class samp {int i, j ;public:// встраиваемый конструкторsamptint a, int b) { i = a; j = b; }int divisible() { return ! { i % j ) ; }Определения функции samp() внутри класса samp достаточно, и никакогодругого определения не требуется.2. Иногда короткие функции могут включаться в объявление класса даже тогда,когда преимущества встраивания мало что дают или вовсе не проявляются.Рассмотрим следующее объявление класса:class myclass {int i;78Самоучитель C++public:myclass(int n) ( i = n; }void show(} { cout « i; }itЗдесь функция show() по умолчанию становится встраиваемой. Однако, каквы, наверное, знаете, операции ввода/вывода, по сравнению с операциямипроцессор/память, являются настолько медленными, что какой бы то ни было эффект от устранения вызова функции практически отсутствует. Однако впрограммах на C++, как правило, можно встретить такие короткие функциивнутри класса.
Делается это просто для удобства, поскольку никакого вредане приносит.1. Переделайте класс stack из раздела 2.1, пример 1, так, чтобы в классе, где этовозможно, использовались встраиваемые функции.2. Переделайте класс strtype из раздела 2.2, пример 3, так, чтобы в классе использовались встраиваемые функции.Проверка усвоенияматериала главы.Теперь вам необходимо выполнить следующие упражнения и ответить навопросы.1. Что такое конструктор? Что такое деструктор? Когда они вызываются?2.