Альтернативы использованию #define в С++? Почему это осуждается?
Автор вопроса: JoelОтветов: 5
Я занимаюсь разработкой C++ меньше года, но за это время я слышал, как несколько человек говорят о том, насколько ужасен #define. Теперь я понимаю, что он интерпретируется препроцессором, а не компилятором, и поэтому не может быть отлажен, но так ли это плохо?
Вот пример (непроверенный код, но общее представление вы поняли):
#define VERSION "1.2"
#include <string>
class Foo {
public:
string getVersion() {return "The current version is "+VERSION;}
};
Почему этот код плохой?
Есть ли альтернатива использованию #define?
Обратное этому вопросу: Зачем использовать #define вместо переменной Matt Ball 21.04.2012
define - это хитрые конструкции, которыми легко злоупотребить или неправильно использовать. но, как и goto, они могут быть очень полезными. Anycorn 21.04.2012
возможный дубликат static const vs #define, в частности этот ответ должно быть достаточно. Anonymous 21.04.2012
Сомневаюсь, что этот код компилируется. Без + было бы, но как есть? Неа. Matthieu M. 21.04.2012
Потому что ВЕРСИЯ может быть перезаписана, и компилятор вам этого не скажет.
Есть ли альтернатива использованию #define?
const char * VERSION = "1.2";
or
const std::string VERSION = "1.2";
Ответ 2 · рейтинг 11 · Konrad Rudolph
Настоящая проблема заключается в том, что определения обрабатываются другим инструментом, отличным от остального языка (препроцессором). Как следствие, компилятор не знает об этом и не может помочь вам, когда что-то пойдет не так - например, повторное использование имени препроцессора.
Рассмотрим случай max, который иногда реализуется как макрос. Как следствие, вы не можете использовать идентификатор max где-либо в своем коде. Где. Но компилятор вам этого не скажет. Вместо этого ваш код будет работать ужасно неправильно, и вы понятия не имеете, почему.
Теперь, при некоторой осторожности, эту проблему можно свести к минимуму (если не полностью устранить). Но для большинства применений #define в любом случае есть лучшие альтернативы, поэтому расчет затрат/выгод становится искаженным: небольшой недостаток вместо отсутствия какой бы то ни было выгоды. Зачем использовать дефектную функцию, если она не дает никаких преимуществ?
Итак, вот очень простая схема:
Нужна константа? Используйте константу (не определение)
Нужна функция? Используйте функцию (не определение)
Нужно что-то, что нельзя смоделировать с помощью константы или функции? Используйте определение, но делайте это правильно.
Делать это "правильно" - само по себе искусство, но есть несколько простых рекомендаций:
Используйте уникальное имя. Все заглавные, всегда с префиксом уникального идентификатора библиотеки. max? Вне. VERSION? Вне. Вместо этого используйте MY_COOL_LIBRARY_MAX и MY_COOL_LIBRARY_VERSION. Например, библиотеки Boost, крупные пользователи макросов, всегда используют макросы, начинающиеся с BOOST_<LIBRARY_NAME>_.
Остерегайтесь оценки. По сути, параметр в макросе - это просто заменяемый текст. Как следствие, #define MY_LIB_MULTIPLY(x) x * x сломан: его можно было использовать как MY_LIB_MULTIPLY(2 + 5), в результате чего получилось 2 + 5 * 2 + 5. Не то, что мы хотели. Чтобы избежать этого, всегда заключайте в скобки все использование аргументов (если только вы не знаете, точно что делаете - спойлер: скорее всего, вы не знаете, что делаете). т; даже эксперты тревожно часто ошибаются в этом).
Правильная версия этого макроса будет:
#define MY_LIB_MULTIPLY(x) ((x) * (x))
Но есть по-прежнему множество способов сделать макросы ужасно неправильными, и, повторяю, компилятор здесь вам не поможет.
Ответ 3 · рейтинг 4 · Kyle
#define не является плохим по своей сути, им просто легко злоупотреблять. Для чего-то вроде строки версии это работает нормально, хотя const char* было бы лучше, но многие программисты используют его для гораздо большего. Например, использование #define в качестве определения типа глупо, когда в большинстве случаев определение типа было бы лучше. Так что в операторах #define нет ничего плохого, и некоторые вещи невозможно сделать без них. Их необходимо оценивать в каждом конкретном случае. Если вы можете найти способ решить проблему без использования препроцессора, вы должны это сделать.
Ответ 4 · рейтинг 2 · EdChum
Я бы не стал использовать #define для определения постоянного ключевого слова use static или еще лучше const int kMajorVer = 1;const int kMinorVer = 2; ИЛИ const std::string kVersion = "1.2";
У Херба Саттера есть отличная статья, в которой подробно описывается, почему #define - это плохо, и приводится несколько примеров, где действительно нет другого способа добиться того же самого: http://www.gotw.ca/gotw/032.htm.
В принципе, как и со многими другими вещами, все в порядке, если вы используете его правильно, но им легко злоупотреблять, а макро-ошибки особенно загадочны и неудобны для отладки.
Я лично использую их для кода условной отладки, а также для вариантов представления данных, которые подробно описаны в конце статьи о саттере.
Ответ 5 · рейтинг 1 · Andrew Tomazos
В общем, препроцессор плох, потому что он создает небезопасный двухпроходный процесс компиляции, создает трудные для декодирования сообщения об ошибках и может привести к трудночитаемому коду. Вы не должны использовать его, если это возможно:
const char* VERSION = "1.2"
Однако есть случаи, когда невозможно сделать то, что вы хотите, без препроцессора: