Следует ли мне избегать использования #define в C ++? Почему и какие альтернативы я могу использовать?

Для некоторых типов программ мне нужно использовать постоянное высокое значение, чтобы указать некоторые свойства некоторых переменных. Я имею в виду, пусть color[i] = 1000000;, если узел i в дереве не исследован. Но я довольно часто неправильно записываю количество нулей в конце, поэтому я просто подумал, не лучше ли сделать это так:

#define UNEXPLORED 1000000;
color[i] = UNEXPLORED;

Я помню, что где-то читал, что лучше избегать использования #define. Это правильно? Как бы вы справились с этой проблемой?


person gen    schedule 05.03.2013    source источник
comment
Пройдите эту ветку. Лучший ответ оттуда - #define's не соблюдайте область действия - stackoverflow.com/questions/1944041/   -  person Mudassir Hasan    schedule 05.03.2013


Ответы (3)


Для простых констант вы можете использовать const или новый constexpr:

constexpr unsigned int UNEXPLORED = 1000000;

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

person Some programmer dude    schedule 05.03.2013
comment
Есть ли какая-то особая причина для использования здесь auto? Основное использование auto, которое я вижу, - это избегать повторения имен типов или объяснения сложных вложенных типов. Это ни то, ни другое, и явное указание типа здесь улучшает ясность. (Точно так же почему constexpr, а не традиционный const?) - person James Kanze; 05.03.2013
comment
@JamesKanze - использование здесь auto приведет к созданию целочисленного типа, достаточно большого для хранения фактического значения. unsigned int не обязательно достаточно велик, чтобы вместить 1000000, проблема, которой нет у макроса. - person Pete Becker; 05.03.2013
comment
@PeteBecker Это интересный аспект, о котором я не подумал. В зависимости от контекста это можно считать недостатком или преимуществом. (Я предполагаю, что если 1000000 не поместился в unsigned int, компилятор по крайней мере предупредит.) - person James Kanze; 05.03.2013
comment
@JamesKanze - Я знаю, что бью дохлую лошадь, но в большинстве случаев макрос работает нормально, без необходимости обходного пути. <g> - person Pete Becker; 05.03.2013
comment
@PeteBecker Как вы заметили, макрос делает то же самое, что и auto. (Почти: если встроенная функция выполняет push_back его в вектор, #define - единственное решение, которое не имеет неопределенного поведения. Или константа enum, но это действительно оправдано только в том случае, если у вас их много, что вы хотите сгруппировать или ограничить область видимости, скажем, в классе.) Использование явного типа гарантирует, что константа имеет этот тип. Что иногда может быть полезно. - person James Kanze; 05.03.2013

Например, используйте константы.

const unsigned int UNEXPLORED = 1000000;

или перечисляет

enum { UNEXPLORED = 1000000 };
person ForEveR    schedule 05.03.2013
comment
Меня это все больше сбивает с толку. :( Я пытаюсь изучить определенную часть, а затем приходят тысячи других. :( В чем разница между ENUM и STRUCT? Что использовать для чего? - person gen; 05.03.2013
comment
@gen По таким вопросам я предлагаю вам взять хорошую книгу для руководства вашим обучением. - person Angew is no longer proud of SO; 05.03.2013
comment
Первый не работает в системах, где unsigned int слишком мал, чтобы вместить 1000000. А создавать enum только для одной-единственной константы неприятно. <g> - person Pete Becker; 05.03.2013

При использовании констант два приведенных выше ответа верны, однако #define не ограничивается только этим использованием. Другой пример использования #define - макросы.

Макросы

Макросы - это фрагменты кода, используемые препроцессором, и в этом отношении они работают точно так же, как и другие объявления #define. Препроцессор буквально заменит вхождение вашего определенного символа кодом макроса. Пример:

#define HELLO_MAC do{ std::cout << "Hello World" << std::endl; }while(false)

int main(int argc, char** argv)
{
     HELLO_MAC;
}

Это буквально заменит символ HELLO_MAC на объявленный мною код. Если бы он был константой, он бы делал то же самое. Таким образом, вы можете рассматривать #defines для констант как особый вид макроса.

С помощью макросов вы также можете передавать параметры, и я считаю, что это особенно полезно для применения политик журналирования / исключений в коде. Например

#define THROW_EXCEPT( ex_type, ex_msg ) /
    do{ throw ex_type( buildExString( (ex_msg), __LINE__, __FILE__ ) ); }while(false)

... 
// somewhere else
THROW_EXCEPT( std::runtime_error, "Unsupported operation in current state" );

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

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

Где нельзя использовать макросы? Где угодно можно использовать что-нибудь другое. Макросы, как и любые #define, предварительно обрабатываются, поэтому компилятор их вообще не видит. Это означает, что никогда не создаются символы для HELLO_MAC или THROW_EXCEPT, и поэтому они не могут быть видны в отладчике. Они также могут сбивать с толку, если вы получаете ошибки компиляции, особенно если это длинные макросы.

person Dennis    schedule 05.03.2013