Я макросы тоже не люблю использовать, но иногда бывает, что они нужны. Ооочень редко
К счастью, в C++ они действительно требуются очень редко (тем, кто реально знает и понимает, зачем добавили пару плюсов), а в более современных языках и вообще отсутствуют. К несчастью, в старом добром C макросы совершенно необходимы для нетривиальных программ, и с ними приходится мириться. Например, посмотрите реализацию
CException или
Protothreads, там они буквально на каждом шагу.
Почему в С++ можно обойтись без макросов, а в С нет?
Как заметил
Алексей++, "они нужны. Ооочень редко"(С). Давайте разберем несколько типичных случаев использования макросов и решим, нужны они реально или нет.
1. Защита заголовочных файлов от множественных включений:
#ifndef MYHEADER_H
#define MYHEADER_H
.
.
.
#endif
В различных реализациях компиляторов имеются прагмы, которые препятствуют повторному включению заголовка при обработке препроцессором (нечто вроде
#pragma once), но они не портабельны, как и полагается прагмам. В данном случае использование макроса вполне оправданно, поскольку правильно работает на любой платформе. К тому же идиома давно устоялась и вызовет недоумения разве что у самого зеленого новичка.
2. Определение констант:
#define PI 3.1415927
Выглядит гораздо читабельнее, чем "магические числа", в изобилии разбросанные по программе, а в случае, если константу еще и потребуется переопределить, вообще единственный способ сохранить душевное здоровье программиста. Однако есть более вразумительный способ объявить константу - квалификатор
const, который к тому же дает компилятору больше возможностей для оптимизации: поместить ли единственый экземпляр константы в память только для чтения и ссылаться на него, либо подставлять значение константы явно в каждой точке, где оно требуется. Препроцессор альтернатив не предоставляет.
Иногда "сишники" определяют отдельные константы через
enum, но это далеко не всегда дает хороший стиль в итоге.
3. Повторяющиеся области кода:
#define UGLY_MACRO { \
a(); \
b(); \
c(); \
}
(Хотя мы уже умудрены опытом и знаем, что всю эту лабуду нужно обернуть одноразовым циклом, если не хотим нарваться на невразумительную ошибку при компиляции; впрочем, сейчас речь не об этом).
Гораздо лучше использовать спецификатор функции
inline, который рекомендует компилятору вместо вызова онлайновой функции развернуть ее тело в точке вызова; впрочем, строптивый компилятор может и не послушаться, если сочтет, что для выбранного способа оптимизации вызов функции обойдется дешевле.
4. Можно приспособить макрос для генерации вариантов кода в зависимости от некоего условия (примеры приводить не буду, ибо громоздко). Гораздо лучше с этим справятся перегруженные функции, шаблоны (template), а также виртуальные методы.
Некоторые из перечисленных языковых средств имеются не только в C++, но и в поздних редакциях C (например, C99). Словом, чем дальше язык от стандарта Кернигана и Ричи, тем меньшую роль в нем играют макросы, хотя совсем на ноль все же не сходят.
По мне, сложные макросы вносят путаницу
Совершенно верно. Кроме того, их бывает трудно раскрывать в уме, чтобы понять, чем именно недоволен компилятор. А уж тестировать макросы и подавно неблагодарное занятие.