Нужно ли #undef местный #define? Есть ли такая вещь, как локальное определение? - PullRequest
13 голосов
/ 20 февраля 2011

Иногда, чтобы было легче писать и читать, я пишу некоторые локальные # define макросы внутри функций (for example, #define O_REAL Ogre::Real).

Нужно ли # undef локальный # определить , чтобы он оставался в определенном блоке кода? Или он автоматически # undef , когда выходит из области действия ? У него даже есть понятие scope ?

Я не уверен, как # define работает в этом случае. Теперь я, конечно, экспериментировал с кодом и пришел к определенным выводам, но поскольку я не уверен, мне бы хотелось получить мнение / совет эксперта.

Ответы [ 6 ]

25 голосов
/ 20 февраля 2011

#define не соответствует ни одной области видимости C ++. Нет такой вещи как "местный" #define. Это будет действовать до тех пор, пока оно не будет #undef -ед. Механизм макросов препроцессора аналогичен функциональности «найти и заменить», присутствующей в большинстве текстовых редакторов; он не уважает содержимое файла.

Другими словами, если вы хотите, чтобы ваш #define был локальным в пределах определенного блока кода, вы должны #undef указать его в конце этого блока из-за того, что макросы не «понимают» область действия.

Фактически, это одна из главных причин, по которой макросы не рекомендуется использовать, если они не являются абсолютно необходимыми в C ++. Именно поэтому имена макросов обычно вводятся в UPPER_CASE, чтобы указать, что это на самом деле макрос.


На самом деле существует довольно много решений без макросов для вашей конкретной ситуации. Учтите следующее:

namespace ReallyLongOuterNamespace
{
    namespace ReallyLongInnerNamespace
    {
        class Foo {};
        void Bar() {}
    };
}

void DoThis()
{
    // Too much typing!
    ReallyLongOuterNamespace::ReallyLongInnerNamespace::Foo f;
    ReallyLongOuterNamespace::ReallyLongInnerNamespace::Bar();
}

Вы можете использовать псевдонимы пространства имен :

void DoThis()
{
    namespace rlin = ReallyLongOuterNamespace::ReallyLongInnerNamespace;

    rlin::Foo f;
    rlin::Bar();
}

Вы также можете использовать typedef s:

void DoThis()
{
    typedef ReallyLongOuterNamespace::ReallyLongInnerNamespace::Foo MyFoo;

    MyFoo f;
}

Вы также можете использовать using декларации:

void DoThis()
{
    using ReallyLongOuterNamespace::ReallyLongInnerNamespace::Foo;
    using ReallyLongOuterNamespace::ReallyLongInnerNamespace::Bar;

    Foo f;
    Bar();
}

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

void DoThis()
{
    namespace rlin = ReallyLongOuterNamespace::ReallyLongInnerNamespace;
    typedef rlin::Foo MyFoo;
    using rlin::Bar;

    MyFoo f;
    Bar();
}

Что касается Ogre::Real, то оно выглядит как typedef для float или double. Вы по-прежнему можете использовать псевдонимы пространства имен, объявления typedef s и using с typedef s:

void UseOgre()
{
    typedef Ogre::Real o_Real; // Yes, you can typedef typedefs.
    using Ogre::Real;
    /* Or, you can use:
    namespace o = Ogre;
    typedef o::Real o_Real;
    using o::Real;
    */

    // All equivalent
    Ogre::Real r1;
    o_Real r2;
    Real r3;
    o::Real r4;
}
2 голосов
/ 20 февраля 2011

К сожалению, #define s не соблюдают правила области видимости.#define s, которые не #undef ', будут влиять на весь код после них.Кроме того, если в коде перед вами определено то же имя макроса, у вас будут проблемы.В C ++ обычно можно избежать необходимости использовать такие локальные макросы с локальными typedefs и ссылками.Например, вы можете сделать:

void foo() {
    typedef Ogre::Real O_REAL;
    // ... 
}

Это будет соблюдать правила области видимостиДля переменных вы можете использовать ссылки:

void foo() {
    int &BAR = Foo::quux::baz::static_bar;
    // ...
}
2 голосов
/ 20 февраля 2011

Макрос scope макроса является частью модуля компиляции, который следует после #define, вплоть до конца модуля (то есть до конца файла .cpp).Однако в Visual C ++ есть пара #pragma push_macro / pop_macro, которую можно использовать в случае, когда определения макросов перекрываются.Вы можете выдвинуть предыдущее определение, определить свое собственное, использовать его, и когда вы решите просто выдвинуть предыдущее определение.

1 голос
/ 20 февраля 2011

нет такой вещи как local define. Определяет всегда предварительное зло; -)

Для вашего примера я бы порекомендовал typedef сделать псевдоним для имен переменных

Однако, иногда local defines - хорошая вещь для программиста (но никогда не для сопровождающего). Чтобы сделать вещи немного проще и безопаснее, я всегда undef это делаю и даже охраняю их запись:

#if defined(LOCAL_CROWBAR)
#error Hurgh!
#endif /* LOCAL_CROWBAR */
#define LOCAL_CROWBAR

... use LOCAL_CROWBAR ...

#undef LOCAL_CROWBAR

Тем не менее, избегайте тех, когда это возможно!

1 голос
/ 20 февраля 2011

# define обрабатывается препроцессором, который вообще не знает синтаксиса Си. #Define игнорирует область действия и будет действовать до тех пор, пока вы не #dedefе его или до конца модуля компиляции.

0 голосов
/ 20 февраля 2011

Нет. Нет таких вещей как "местный" #define с.

Область, в которой находится #define, неизвестна в то время, когда они обрабатываются - хорошо - препроцессором. (Следовательно, иногда вы слышите термин «директива препроцессора» вместо просто «макрос».)

...