Мы все определенно использовали typedef
s и #define
s один или другой раз. Сегодня, работая с ними, я начал задумываться о чем-то.
Рассмотрим следующие 2 ситуации для использования int
типа данных с другим именем:
typedef int MYINTEGER
а также
#define MYINTEGER int
Как и в приведенной выше ситуации, во многих ситуациях мы можем очень хорошо выполнить задачу, используя #define, а также сделать то же самое, используя typedef, хотя способы, которыми мы делаем то же самое, могут быть совершенно разными. #define также может выполнять действия MACRO, которые typedef не может.
Хотя основная причина их использования различна, насколько отличается их работа? Когда один должен быть предпочтительнее другого, когда оба могут быть использованы? Кроме того, гарантированно один будет быстрее, чем другой, в каких ситуациях? (например, #define является директивой препроцессора, поэтому все делается намного раньше, чем при компиляции или во время выполнения).
Ответы:
A
typedef
обычно предпочтительнее, если нет какой-то странной причины, по которой вам нужен макрос.макросы выполняют текстовую подстановку, что может существенно нарушить семантику кода. Например, учитывая:
Вы могли бы на законных основаниях написать:
потому что
short MYINTEGER
расширяется доshort int
.С другой стороны, с typedef:
имя
MYINTEGER
- это другое имя для типаint
, а не текстовая подстановка для ключевого слова "int".Вещи становятся еще хуже с более сложными типами. Например, учитывая это:
a
,b
Иc
все указатели, ноd
этоchar
, так как последняя линия расширяется:что эквивалентно
(Определение типов для указателей обычно не очень хорошая идея, но это иллюстрирует суть.)
Еще один странный случай:
источник
typedef
, но правильный способ создать макрос , который делает что - то с тем, что следует это использовать аргументы:#define CHAR_PTR(x) char *x
. Это, по крайней мере, приведет к тому, что компилятор будет некорректно использоваться.const CHAR_PTR mutable_pointer_to_const_char; const char_ptr const_pointer_to_mutable_char;
Безусловно, самая большая проблема с макросами заключается в том, что они не имеют границ. Это само по себе гарантирует использование typedef. Кроме того, это более понятно семантически. Когда кто-то, кто читает ваш код, видит определение, он не будет знать, о чем он, пока не прочитает его и не поймет весь макрос. Определение типа сообщает читателю, что имя типа будет определено. (Я должен упомянуть, что я говорю о C ++. Я не уверен насчет определения типа typedef для C, но я думаю, что это похоже).
источник
Исходный код в основном написан для коллег-разработчиков. Компьютеры используют скомпилированную версию.
В этой перспективе
typedef
есть значение,#define
которого нет.источник
Ответ Кейта Томпсона очень хороший, и вместе с дополнительными замечаниями Тамаса Селеи о сфере охвата вы должны получить всю необходимую информацию.
Вы всегда должны считать макросы самым последним средством отчаянного. Есть определенные вещи, которые вы можете делать только с макросами. Даже тогда вы должны долго и усердно думать, если вы действительно хотите это сделать. Объем отладки, который может быть вызван слегка испорченными макросами, значителен, и вы будете просматривать предварительно обработанные файлы. Стоит сделать это только один раз, чтобы почувствовать масштабы проблемы в C ++ - только размер предварительно обработанного файла может быть откровением.
источник
В дополнение ко всем действительным замечаниям о замене области видимости и текста, #define также не полностью совместим с typedef! А именно, когда дело доходит до указателей на функции.
Это объявляет тип,
fptr_t
который является указателем на функцию типаvoid func(void)
.Вы не можете объявить этот тип с помощью макроса.
#define fptr_t void(*)(void)
очевидно, не будет работать. Вам придется написать что-то непонятное#define fptr_t(name) void(*name)(void)
, что на самом деле не имеет никакого смысла в языке Си, где у нас нет конструкторов.Указатели массива также нельзя объявить с помощью #define:
typedef int(*arr_ptr)[10];
И хотя в C нет языковой поддержки безопасности типов, о которой стоит упомянуть, другой случай, когда typedef и #define несовместимы, - это когда вы делаете подозрительные преобразования типов. Компилятор и / или инструмент астатического анализатора могут выдавать предупреждения для таких преобразований, если вы используете typedef.
источник
char *(*(*foo())[])();
(foo
функция, возвращающая указатель на массив указателей на функции, возвращающие указатель наchar
).Используйте инструмент с наименьшим количеством энергии, который выполняет работу, и тот, который содержит большинство предупреждений. #define оценивается в препроцессоре, вы там в основном сами по себе. typedef оценивается компилятором. Проверки даны, и typedef может определять только типы, как следует из названия. Так что в вашем примере определенно перейдите на typedef.
источник
Помимо обычных аргументов против define, как бы вы написали эту функцию с помощью макросов?
Это, очевидно, тривиальный пример, но эта функция может суммировать по любой прямой итерируемой последовательности типа, которая реализует сложение *. Используемые таким образом определения типов являются важным элементом общего программирования .
* Я только что написал это здесь, я оставляю компиляцию в качестве упражнения для читателя :-)
РЕДАКТИРОВАТЬ:
Этот ответ, кажется, вызывает много путаницы, поэтому позвольте мне сделать это подробнее. Если бы вы заглянули внутрь определения вектора STL, вы бы увидели нечто похожее на следующее:
Использование typedefs в стандартных контейнерах позволяет универсальной функции (например, той, которую я создал выше) ссылаться на эти типы. Функция Sum суммируется с типом контейнера (
std::vector<int>
), а не с типом, содержащимся в контейнере (int
). Без typedef было бы невозможно сослаться на этот внутренний тип.Следовательно, typedefs являются центральными для Modern C ++, а это невозможно с макросами.
источник
typedef
макросах против встроенных функций, а не макросах.typedef
соответствует философии C ++: все возможные проверки / утверждения во время компиляции.#define
это просто прием препроцессора, который скрывает много семантики для компилятора. Вам не следует беспокоиться о производительности компиляции больше, чем о правильности кода.Когда вы создаете новый тип, вы определяете новую «вещь», которой манипулирует домен вашей программы. Таким образом, вы можете использовать эту «вещь» для составления функций и классов, и вы можете использовать компилятор для своей выгоды для выполнения статических проверок. В любом случае, поскольку C ++ совместим с C, существует много неявных преобразований между
int
типами и типами, основанными на int, которые не генерируют предупреждения. Таким образом, вы не получите полную мощность статических проверок в этом случае. Тем не менее, вы можете заменить свойtypedef
на,enum
чтобы найти неявные преобразования. Например: если выtypedef int Age;
, то вы можете заменить на,enum Age { };
и вы получите все виды ошибок путем неявных преобразований междуAge
иint
.Другое дело:
typedef
может быть внутриnamespace
.источник
Использование определений вместо typedefs беспокоит еще один важный аспект, а именно концепция черт типа. Рассмотрим различные классы (например, стандартные контейнеры), каждый из которых определяет свои конкретные определения типов. Вы можете написать общий код, ссылаясь на typedefs. Примеры этого включают общие требования к контейнерам (стандарт c ++ 23.2.1 [container.requirements.general]), например
Все это не может быть выражено в общем виде с помощью макроса, потому что он не ограничен.
источник
Не забывайте последствия этого в вашем отладчике. Некоторые отладчики не очень хорошо справляются с #define. Поиграйте с обоими в отладчике, который вы используете. Помните, что вы потратите больше времени на чтение, чем на написание.
источник
«#define» заменит то, что вы написали после, typedef создаст тип. Так что если вы хотите иметь привычный тип - используйте typedef. Если вам нужны макросы - используйте define.
источник