typedefs и #defines

Мы все определенно использовали typedef s и #define один раз или другой. Сегодня, работая с ними, я начал обдумывать что-то.

Рассмотрим приведенные ниже ситуации для использования типа данных int с другим именем:

typedef int MYINTEGER

и

#define MYINTEGER int

Как и в предыдущей ситуации, во многих ситуациях мы можем очень хорошо выполнить вещь с помощью #define, а также сделать то же самое с помощью typedef, хотя способы, которыми мы делаем то же самое, могут быть совершенно разными. #define также может выполнять действия MACRO, которые не могут быть введены typedef.

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

30 голосов | спросил c0da 18 Jpm1000000pmWed, 18 Jan 2012 15:06:34 +040012 2012, 15:06:34

11 ответов


65

A typedef обычно предпочтительнее, если нет какой-то странной причины, что вам определенно нужен макрос.

Макросы

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

#define MYINTEGER int

вы можете законно написать:

short MYINTEGER x = 42;

, потому что short MYINTEGER расширяется до short int .

С другой стороны, с typedef:

typedef int MYINTEGER:

имя MYINTEGER - другое имя для типа int, а не текстовая подстановка для ключевого слова «int».

Все становится еще хуже с более сложными типами. Например, учитывая это:

typedef char *char_ptr;
char_ptr a, b;
#define CHAR_PTR char*
CHAR_PTR c, d;

a, b и c - все указатели, но d a char, потому что последняя строка расширяется до:

char* c, d;

, что эквивалентно

char *c;
char d;

(Typedefs для типов указателей обычно не очень хорошая идея, но это иллюстрирует точку.)

Другой нечетный случай:

#define DWORD long
DWORD double x;     /* Huh? */
ответил Keith Thompson 18 Jpm1000000pmWed, 18 Jan 2012 15:17:02 +040012 2012, 15:17:02
15

Самой большой проблемой макросов является то, что они не охвачены областью. Это само по себе требует использования typedef. Кроме того, это более ясно семантически. Когда кто-то, кто читает ваш код, видит определение, он не будет знать, что это такое, пока он не прочитает его и не постигает весь макрос. Typedef сообщает читателю, что имя типа будет определено. (Я должен упомянуть, что я говорю о C ++. Я не уверен в том, что для C, но я думаю, это похоже).

ответил Tamás Szelei 18 Jpm1000000pmWed, 18 Jan 2012 15:18:27 +040012 2012, 15:18:27
15

Исходный код в основном написан для других разработчиков. Компьютеры используют скомпилированную версию.

В этой перспективе typedef имеет значение, которое #define нет.

ответил mouviciel 18 Jpm1000000pmWed, 18 Jan 2012 15:44:59 +040012 2012, 15:44:59
6

Ответ Кит Томпсон очень хорош и вместе с Дополнительные пункты Tamás Szelei о области охвата должны предоставить все необходимое вам.

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

ответил Joris Timmermans 18 Jpm1000000pmWed, 18 Jan 2012 15:21:15 +040012 2012, 15:21:15
5

В дополнение ко всем действительным замечаниям об объеме и замене текста, уже указанным, #define также не полностью совместим с typedef! А именно, когда речь идет о указателях функций.

typedef void(*fptr_t)(void);

Объявляет тип fptr_t, который является указателем на функцию типа void func(void)

Вы не можете объявить этот тип макросом. #define fptr_t void(*)(void), очевидно, не будет работать. Вам нужно было бы написать что-то неясное, как #define fptr_t(name) void(*name)(void), что на самом деле не имеет смысла на языке C, где у нас нет конструкторов.

Указатели массива не могут быть объявлены с помощью #define: typedef int(*arr_ptr)[10];


И хотя C не поддерживает языковую поддержку для безопасности типов, другой случай, когда typedef и #define несовместимы, - это когда вы делаете подозрительные преобразования типов. Инструмент компилятора и /или астатического анализатора может давать предупреждения для таких преобразований, если вы используете typedef.

ответил 19 Jam1000000amThu, 19 Jan 2012 11:48:38 +040012 2012, 11:48:38
4

Используйте инструмент с наименьшей мощностью, которая выполняет задание, а другая - с большинством предупреждений. #define оценивается в препроцессоре, вы в основном сами по себе. typedef оценивается компилятором. Проверки выдаются, и typedef может определять типы только по названию. Поэтому в вашем примере определенно перейдите к typedef.

ответил Martin 18 Jpm1000000pmWed, 18 Jan 2012 15:15:46 +040012 2012, 15:15:46
2

Помимо обычных аргументов против define, как бы вы могли написать эту функцию с помощью макросов?

template <typename IterType>
typename IterType::value_type Sum(
    const IterType& begin, 
    const IterType& end, 
    const IterType::value_type& initialValue)
{
    typename IterType::value_type result = initialValue;
    for (IterType i = begin; i != end; ++i)
        result += i;

    return result;
}

....

vector<int> values;
int sum = Sum(values.begin(), values.end(), 0);

Это, очевидно, тривиальный пример, но эта функция может суммироваться по любой последующей итерационной последовательности типа, реализующего дополнение *. Типедифы, используемые таким образом, являются важным элементом Generic Programming .

* Я просто написал это здесь, я оставляю его компилятором как упражнение для читателя: -)

EDIT:

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

template <typename ValueType, typename AllocatorType>
class vector
{
public:
    typedef ValueType value_type;
...
}

Использование typedefs в стандартных контейнерах позволяет использовать общую функцию (например, созданную мной выше) для ссылки на эти типы. Функция «Сумма» настроена на тип контейнера (std::vector<int>), а не на тип, хранящийся внутри контейнера (int). Без typedef было бы невозможно ссылаться на этот внутренний тип.

Поэтому typedefs являются центральными для Modern C ++, и это невозможно с помощью макросов.

ответил Chris Pitman 19 Jpm1000000pmThu, 19 Jan 2012 16:57:34 +040012 2012, 16:57:34
1

typedef соответствует философии C ++: все возможные проверки /утверждения во время компиляции. #define - это просто препроцессорный трюк, который скрывает много семантики компилятору. Вы не должны беспокоиться о производительности компиляции, а не о правильности кода.

При создании нового типа вы определяете новую «вещь», которую манипулирует домен вашей программы. Таким образом, вы можете использовать эту «вещь» для составления функций и классов, и вы можете использовать компилятор для вашей пользы для статических проверок. В любом случае, поскольку C ++ совместим с C, существует много неявного преобразования между int и типами на основе int, которые не генерируют предупреждения. Таким образом, вы не получите полную силу статических проверок в этом случае. Однако вы можете заменить typedef на enum, чтобы найти неявные преобразования. Например: Если у вас есть typedef int Age;, вы можете заменить на enum Age { };, и вы получите всевозможные ошибки путем неявных преобразований между Age и int

Другое: typedef может находиться внутри namespace

ответил dacap 18 Jpm1000000pmWed, 18 Jan 2012 20:31:26 +040012 2012, 20:31:26
1

Использование определений вместо typedefs вызывает беспокойство по другому важному аспекту, а именно по понятию черт типа. Рассмотрим различные классы (подумайте о стандартных контейнерах), все из которых определяют их конкретные typedef. Вы можете написать общий код, обратившись к typedefs. Примеры этого включают общие требования к контейнерам (стандарт c ++ 23.2.1 [container.requirements.general]), например

X::value_type
X::reference
X::difference_type
X::size_type

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

ответил Francesco 18 Jpm1000000pmWed, 18 Jan 2012 22:09:40 +040012 2012, 22:09:40
1

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

ответил anon 21 Jam1000000amSat, 21 Jan 2012 05:48:44 +040012 2012, 05:48:44
-2

"# define" заменит то, что вы написали после, typedef создаст тип. Поэтому, если вы хотите иметь тип custome - используйте typedef. Если вам нужны макросы - используйте define.

ответил Dainius 18 Jpm1000000pmWed, 18 Jan 2012 19:29:48 +040012 2012, 19:29:48

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132