Читаем Программирование. Принципы и практика использования C++ Исправленное издание полностью

Мы используем прописные буквы в имени MAX, чтобы отличить его от многих функций с именем max (в разных программах). Очевидно, что этот макрос сильно отличается от функции: у него нет типов аргументов, нет тела, нет инструкции return и так далее, и вообще, зачем здесь так много скобок? Проанализируем следующий код:

int aa = MAX(1,2);

double dd = MAX(aa++,2);

char cc = MAX(dd,aa)+2;

Он разворачивается в такой фрагмент программы:

int aa = ((1)>=( 2)?(1):(2));

double dd = ((aa++)>=(2)?( aa++):(2));

char cc = ((dd)>=(aa)?(dd):(aa))+2;

Если бы всех этих скобок не было, то последняя строка выглядела бы следующим образом.

char cc = dd>=aa?dd:aa+2;

Иначе говоря, переменная cc могла бы легко получить другое значение, которого вы не ожидали, исходя из определения макроса. Определяя макрос, не забывайте заключить в скобки каждый аргумент, входящий в выражение.

  С другой стороны, не всегда скобки могут спасти нас от второго варианта развертывания. Параметру макроса x было присвоено значение aa++, а поскольку переменная x в макросе MAX используется дважды, переменная a может инкрементироваться также дважды. Не передавайте макросу аргументы, имеющие побочные эффекты.

Какой-то “гений” определил макрос следующим образом и поместил его в широко используемый заголовочный файл. К сожалению, он также назвал его max, а не MAX, поэтому когда в стандартном заголовке языка C++ объявляется функция

template inline T max(T a, T b) { return a

имя max разворачивается с аргументами T a и T b, и компилятор видит строку

template inline T ((T a)>=(T b)?(T a):(T b))

  { return a

Сообщения об ошибке, выдаваемые компилятором, интересны, но не слишком информативны. В случае опасности можете отменить определение макроса.

#undef max

К счастью, этот макрос не привел к большим неприятностям. Тем не менее в широко используемых заголовочных файлах существуют десятки тысяч макросов; вы не можете отменить их все, не вызвав хаоса.

Не все параметры макросов используются как выражения. Рассмотрим следующий пример:

#define ALLOC(T,n) ((T*)malloc(sizeof(T)*n))

Это реальный пример, который может оказаться очень полезным для предотвращения ошибок, возникающих из-за согласованности между желательным типом выделяемой памяти и использованием оператора sizeof.

double* p = malloc(sizeof(int)*10); /* похоже на ошибку */

К сожалению, написать макрос, который позволял бы выявить исчерпание памяти, — нетривиальная задача. Это можно было бы сделать, если бы мы в каком-то месте программы соответствующим образом определили переменную error_var и функцию error.

#define ALLOC(T,n) (error_var = (T*)malloc(sizeof(T)*n), \

                   (error_var==0)\

                   ?(error("Отказ выделения памяти"),0)\

                   :error_var)

Строки, завершающиеся символом \, не содержат опечаток; это просто способ разбить определение макроса на несколько строк. Когда мы пишем программы на языке C++, то предпочитаем использовать оператор new.

<p id="AutBody_Root563"><strong>27.8.2. Синтаксис макросов</strong></p>

Можно определить макрос, который приводит текст исходного кода в приятный для вас вид. Рассмотрим пример.

#define forever for(;;)

#define CASE break; case

#define begin {

#define end }

  Мы резко протестуем против этого. Многие люди пытались делать такие вещи. Они (и люди, которым пришлось поддерживать такие программы) пришли к следующим выводам.

• Многие люди не разделяют ваших взглядов на то, что считать лучшим синтаксисом.

• Улучшенный синтаксис является нестандартным и неожиданным; остальные люди будут сбиты с толку.

• Использование улучшенного синтаксиса может вызвать непонятные ошибки компиляции.

• Текст программы, который вы видите перед собой, не совпадает с текстом, который видит компилятор, и компилятор сообщает об ошибках, используя свой словарный запас, а не ваш.

Не пишите синтаксические макросы, для того чтобы улучшить внешний вид вашего кода. Вы и ваши лучшие друзья могут считать его превосходным, но опыт показывает, что вы окажетесь в крошечном меньшинстве среди более крупного сообщества программистов, поэтому кому-то придется переписать ваш код (если он сможет просуществовать до этого момента). 

<p id="AutBody_Root564"><strong>27.8.3. Условная компиляция</strong></p>

Представьте себе, что у вас есть два варианта заголовочного файла, например, один — для операционной системы Linux, а другой — для операционной системы Windows. Как выбрать правильный вариант в вашей программе? Вот как выглядит общепринятое решение этой задачи:

#ifdef WINDOWS

Перейти на страницу:

Похожие книги

97 этюдов для архитекторов программных систем
97 этюдов для архитекторов программных систем

Успешная карьера архитектора программного обеспечения требует хорошего владения как технической, так и деловой сторонами вопросов, связанных с проектированием архитектуры. В этой необычной книге ведущие архитекторы ПО со всего света обсуждают важные принципы разработки, выходящие далеко за пределы чисто технических вопросов.?Архитектор ПО выполняет роль посредника между командой разработчиков и бизнес-руководством компании, поэтому чтобы добиться успеха в этой профессии, необходимо не только овладеть различными технологиями, но и обеспечить работу над проектом в соответствии с бизнес-целями. В книге более 50 архитекторов рассказывают о том, что считают самым важным в своей работе, дают советы, как организовать общение с другими участниками проекта, как снизить сложность архитектуры, как оказывать поддержку разработчикам. Они щедро делятся множеством полезных идей и приемов, которые вынесли из своего многолетнего опыта. Авторы надеются, что книга станет источником вдохновения и руководством к действию для многих профессиональных программистов.

Билл де Ора , Майкл Хайгард , Нил Форд

Программирование, программы, базы данных / Базы данных / Программирование / Книги по IT