Директиву #define можно также использовать дгш создания макросов. Макрос — это лексема, созданная с помощью директивы #define. Он принимает параметры подобно обычной функции. Препроцессор заменяет строку подстановки любым заданным параметром. Например, макрокоманду TWICE можно определить следующим образом:
#define TWICE(x) ( (x) * 2 )
А затем в программе можно записать следующую строку:
TWICE(4)
Целая строка TWICE(4) будет удалена, а вместо нее будет стоять значение 8! Когда препроцессор считывает параметр 4, он выполняет следующую подстановку: ((4) * 2), это выражение затем вычисляется как 4 * 2 и в результате получается число 8.
Макрос может иметь больше одного параметра, причем каждый параметр в тексте замены может использоваться неоднократно. Вот как можно определить два часто используемых макроса — МАХ и MIN:
#define MAX(x,y) ( (x) > (у) ? (x) : (у) )
#define MIN(x,y) ( (x) < (у) ? (x) : (у) )
Обратите внимание, что в определении макроса открывающая круглая скобка для списка параметров должна немедленно следовать за именем макроса, т.е. между ними не должно быть никаких пробелов. Препроцессор, в отличие от компилятора, не прощает присутствия ненужных пробелов. Если записать
#define MAX (x,y) ( (x) > (у) ? (x) : (у) )
и попытаться использовать макрос МАХ
int x = 5, у = 7, z;
z = MAX(x,y);
то промежуточный код будет иметь следующий вид:
int x = 5, у = 7, z;
z = (x,y) ( (x) > (у) ? (x) : (у) )(x,y)
В этом случае сделана простая текстовая замена, а не вызов макроса, т.е. лексема МАХ была заменена выражением (x,y) ( (x) > (у) ? (x) : (у) ),за которым сохранилась строка (x, у).
Однако после удаления пробела между словом МАХ и списком параметров (x,y) промежуточный код выглядит уже по-другому:
int x = 5, у = 7, z;
z =7;
Зачем нужны все эти круглые скобки
Вам может показаться странным, что в макросах используется так много круглых скобок. На самом деле препроцессор совсем не требует, чтобы вокруг параметров в строке подстановки ставились круглые скобки, но эти скобки помогают избежать нежелательных побочных эффектов при передаче макросу сложных значений. Например, если определить МАХ как
#define MAX(x,y) x > у ? x : у
и передать значения 5 и 7, то макрос МАХ будет нормально работать. Но если передать более сложные выражения, можно получить неожиданные результаты, как показано в листинге 21.2.
Листинг 21.2. Использование в макросе круглых скобок
1: // Листинг 21.2. Использование в макросе круглых скобок
2: #include
3:
4: #define CUBE(a) ( (а) * (а) << (а) )
5: #define THREE(a) а * а * а 6:
7: int main()
8: {
9: long x = 5;
10: long у = CUBE(x);
11: long z = THREE(x);
12:
13: cout << "у: " << у << endl;
14: cout << "z: " << z << endl;
15:
16: long а = 5, b = 7;
17: у = CUBE(a+b);
18: z = THREE(a+b);
19:
20: cout << "у: " << у << endl;
21: cout << "z: " << z << endl;
22: return 0;
23: }
Результат:
у: 125
z: 125
у: 1728
z: 82
Анализ:В строке 4 определяется макрос CUBE с параметром x, который заключается в круглые скобки при каждом его использовании в выражении. В строке 5 определяется макрос THREE, параметр которого используется без круглых скобок.
При первом использовании этих макросов параметру передается значение 5, и оба макроса прекрасно справляются со своей работой. Макрос CUBE(5) преобразуется в выражение ( (5) * (5) * (5) ), которое при вычислении дает значение 125, а макрос THREE(5) преобразуется в выражение 5 * 5 * 5, которое также возвращает значение 125.
При повторном обращении к этим макросам в строках 16—18 параметру передается выражение 5 + 7. В этом случае макрос CUBE(5+7) преобразуется в следующее выражение:
( (5+7) * (5+7) * (5+7) )
Оно соответствует выражению
( (12) * (12) * (12) )
При вычислении этого выражения получаем значение 1728. Однако макрос THREE(5+7) преобразуется в выражение иного вида:
5 + 7 * 5 + 7 * 5 + 7
А поскольку операция умножения имеет более высокий приоритет по сравнению с операцией сложения, то предыдущее выражение эквивалентно следующему:
5 + (7 * 5) + (7 * 5) + 7
После вычисления произведений в круглых скобках получаем выражение
5 + (35) + (35) + 7
После суммирования оно возвращает значение 82.
Макросы в сравнении с функциями шаблонов
При работе с макросами и языке C++ можно столкнуться с четырьмя проблемами. Первая состоит в возможных неудобствах при увеличении самого выражения макроса, поскольку любой макрос должен быть определен в одной строке. Безусловно, эту строку можно продлить с помощью символа обратной косой черты (\), но большие макросы сложны для понимания и с ними трудно работать.
Читать дальше