Преимущество макроса над встроенным в C ++ - PullRequest
7 голосов
/ 25 января 2011

Мы знаем, что in-line выгодно, так как они проверяются компилятором, и одна и та же операция (например, ++ x) не оценивается более одного раза при передаче в качестве аргумента по сравнению с макросами.

НоВ одном из интервью меня спросили о конкретных преимуществах или обстоятельствах, когда макрос более благоприятен для встраивания в C ++.

Кто-нибудь знает ответ или может дать ответ на этот вопрос?

Ответы [ 9 ]

14 голосов
/ 25 января 2011

Единственное, о чем я могу думать, - это некоторые приемы, которые вы можете сделать с помощью макроса, которые нельзя сделать с помощью встроенной функции.Склейка токенов во время компиляции и подобные хакерские атаки.

8 голосов
/ 25 января 2011

Вот особая ситуация, когда макросы не только предпочтительны, но и являются единственным способом достичь чего-либо.

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

LogError("Something Bad!", __FILE__, __LINE__);

... или, если вы хотите, чтобы он работал автоматически, вы должны полагаться на макрос (предупреждение: не скомпилировано):

#define LogErrorEx(ERR) (LogError(ERR, __FILE__, __LINE__))
// ...
LogErrorEx("Something Else Bad!");

Этого нельзя достичь с помощью шаблонов, параметров по умолчанию, конструкции по умолчанию или любого другого устройства в C ++.

4 голосов
/ 25 января 2011

Иногда вы хотите расширить язык способами, которые невозможны никаким другим методом.

#include <iostream>

#define CHECK(x) if (x); else std::cerr << "CHECK(" #x ") failed!" << std::endl

int main() {
    int x = 053;
    CHECK(x == 42);
    return 0;
}

Это печатает CHECK(x == 42) failed!.

1 голос
/ 25 января 2011

Встроенные функции, как видно из названия, ограничены функциональными задачами, выполнением некоторого кода.

Макросы имеют гораздо более широкое приложение, которое они могут расширить, например, до объявлений или заменить целые языковые конструкции. Некоторые примеры (написанные для C и C ++), которые нельзя сделать с помощью функций:

typedef struct POD { double a; unsigned b } POD;
#declare POD_INITIALIZER { 1.0, 37u }

POD myPOD = POD_INITIALIZER;

#define DIFFICULT_CASE(X) case (X)+2 :; case (X)+3
#define EASY_CASE(X) case (X)+4 :; case (X)+5

switch (a) {
   default: ++a; break;
   EASY_CASE('0'): --a; break;
   DIFFICULT_CASE('A'): a = helperfunction(a); break;
}

#define PRINT_VALUE(X)                        \
do {                                          \
 char const* _form = #X " has value 0x%lx\n"; \
 fprintf(stderr, _form, (unsigned long)(X));  \
} while (false)

В контексте C ++ в Boost есть много других примеров, которые более сложны и полезны.

Но поскольку с такими макросами вы каким-то образом расширяете язык (не обязательно, препроцессор является его частью), многие люди не любят макросы, особенно в сообществе C ++, чуть меньше в сообществе C.

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

1 голос
/ 25 января 2011

Как уже говорилось, макросы могут использовать директивы препроцессора: например, __FILE__, __LINE__, но, конечно, #include и #define также могут быть полезны для поведения параметра:

#ifdef __DEBUG__
#    define LOG(s) std:cout << s << std:endl
#else
#    define LOG(s)
#endif

В зависимости от того, определено или нет __DEBUG__ (через #define или через опции компилятора), макрос LOG будет активным или нет. Это простой способ иметь отладочную информацию в вашем коде, которую можно легко деактивировать.

Можно также подумать об изменении способа выделения памяти (malloc будет переопределено для назначения пула памяти вместо стандартной кучи, например, и т. Д.).

1 голос
/ 25 января 2011

В частности, в C ++ одним из случаев использования MACRO, которые кажутся всплывающими очень часто (за исключением печати отладки с файлом и строкой), является использование MACRO для заполнения набора стандартных методов в классе, который не может быть унаследован от Базовый класс. В некоторых библиотеках, которые создают собственные механизмы RTTI, сериализации, шаблонов выражений и т. Д., Они часто полагаются на набор статических переменных const и статических методов (и, возможно, специальной семантики для некоторых перегруженных операторов, которые не могут быть унаследованы), которые почти всегда являются то же самое, но необходимо добавить к любому классу, который пользователь определяет в этой структуре. В этих случаях MACRO часто предоставляются так, что пользователю не нужно беспокоиться о вводе всего необходимого кода (ему нужно только вызвать MACRO с необходимой информацией). Например, если я создаю простую систему RTTI (идентификация типа во время выполнения), мне может потребоваться, чтобы все классы имели TypeID и были динамически преобразованы:

class Foo : public Bar {
  MY_RTTI_REGISTER_CLASS(Foo, Bar, 0xBAADF00D)
};   

#define MY_RTTI_REGISTER_CLASS(CLASSNAME,BASECLASS,UNIQUEID) \
  public:\
    static const int TypeID = UNIQUEID;\
    virtual void* CastTo(int aTypeID) {\
      if(aTypeID == TypeID)\
        return this;\
      else\
        return BASECLASS::CastTo(aTypeID);\
    };

Вышесказанное нельзя сделать с помощью шаблонов или наследования, и это облегчает жизнь пользователя и позволяет избежать повторения кода.

Я бы сказал, что этот вид использования MACRO является наиболее распространенным в C ++.

0 голосов
/ 10 апреля 2012
    #include <stdio.h>
    #define sq(x) x*x
    int main()
    {  
        printf("%d", sq(2+1));
        printf("%d", sq(2+5));
        return 0;
    }

Выходные данные для этого кода 5 и 17. Макросы расширяются в текстовом виде.Это не похоже на функции.

Объяснение для этого примера:

sq (2 + 1) = 2 + 1 * 2 + 1 = 2 + 2 + 1 = 5

sq (2 + 5) = 2 + 5 * 2 + 5 = 2 + 10 + 5 = 17

0 голосов
/ 25 января 2011

Я бы добавил два варианта использования:

  1. MIN и MAX до C ++ 0x, потому что тип возвращаемого значения должен был быть объявлен вручную, смешанный min и max поскольку встроенные функции были бы кошмарными, в то время как простой макрос делал это в мгновение ока.
  2. конфиденциальность : вы всегда можете undef макрос перед выходом из заголовка, выне может "отменить" встроенную функцию (или другой символ).Это связано с отсутствием надлежащей модульности в языках C и C ++.
0 голосов
/ 25 января 2011

Макрос похож на определение замены текста.

Эти существенные различия, которые приходят мне в голову:

  • Оно не должно быть функциональным. Я имею в виду, что он не обязательно должен содержать какой-то непротиворечивый набор скобок, например.
  • Может использоваться в другом месте. Как в области объявления класса или даже в глобальной области видимости. Так что это не должно входить в сферу действия другой функции.

Вы должны использовать их, если хотите выполнить действия, которые невозможно выполнить с помощью функций:

  • инициализация сложных таблиц (делает ядро ​​более читабельным)
  • облегчить объявление некоторых специальных членов, таких как идентификаторы событий или классы тегов (часто используется в MFC IMPLEMENT_DYNAMIC)
  • сжимать повторяющиеся объявления в начале функций
  • уже упоминавшееся использование __LINE__, __FILE__, ... для ведения журнала
...