Разница между «struct» и «typedef struct» в C ++?

707

В С++ существует ли разница между:

struct Foo { ... };

и

typedef struct { ... } Foo;
Теги:
struct
typedef

8 ответов

1049
Лучший ответ

В C++ есть только тонкая разница. Это удержание от C, в котором это имеет значение.

Стандарт C-языка (C89 §3.1.2.3, C99 §6.2.3 и C11 §6.2.3) требует отдельных пространств имен для разных категорий идентификаторов, включая идентификаторы тегов (для struct/union/enum) и обычные идентификаторы (для typedef и другие идентификаторы).

Если вы только что сказали:

struct Foo { ... };
Foo x;

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

Вы должны объявить это как:

struct Foo x;

Каждый раз, когда вы хотите обратиться к Foo, вам всегда нужно называть это struct Foo. Это раздражает быстро, поэтому вы можете добавить typedef:

struct Foo { ... };
typedef struct Foo Foo;

Теперь struct Foo (в пространстве имен тегов) и просто Foo (в пространстве имен обычных имен) относятся к одной и той же вещи, и вы можете свободно объявлять объекты типа Foo без ключевого слова struct.


Конструкция:

typedef struct Foo { ... } Foo;

это просто аббревиатура для декларации и typedef.


В заключение,

typedef struct { ... } Foo;

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


В C++ все объявления struct/union/enum/class действуют так, как будто они неявно typedef, пока имя не скрыто другим объявлением с тем же именем. См. Ответ Майкла Берра для получения полной информации.

  • 48
    В то время как то, что вы говорите, верно, AFAIK, утверждение, 'typedef struct {...} Foo;' создает псевдоним для неназванной структуры.
  • 24
    Хороший улов, есть небольшая разница между "typedef struct Foo {...} Foo;" и "typedef struct {...} Foo;".
Показать ещё 14 комментариев
209

В этой статье DDJ Дэн Сакс объясняет одну небольшую область, где ошибки могут проскальзывать, если вы не набираете свои структуры (и классы!)

Если вы хотите, вы можете себе представить, что С++ генерирует typedef для каждого тега имя, например

typedef class string string;

К сожалению, это не совсем точный. Хотелось бы, чтобы все было так просто, но это не так. С++ не может генерировать такие typedefs для структур, союзов или перечислений без введения несовместимости с C.

Например, предположим, что программа C объявляет как функцию, так и структуру Именованный статус:

int status(); struct status;

Опять же, это может быть плохая практика, но это C. В этой программе статус (по само) относится к функции; структура статус относится к типу.

Если С++ автоматически генерирует typedefs для тегов, тогда, когда вы скомпилировал эту программу как С++, компилятор будет генерировать:

typedef struct status status;

К сожалению, это имя типа конфликт с именем функции и программа не будет компилироваться. Это почему С++ не может просто генерировать typedef для каждого тега.

В С++ теги действуют так же, как typedef имена, за исключением того, что программа может объявить объект, функцию или перечислитель с тем же именем и та же область действия, что и тег. В этом случае имя объекта, функции или имени счетчика скрывает имя тега. Программа может обратитесь к имени тега только с помощью ключевое слово class, struct, union или перечисление (по мере необходимости) перед название тэга. Название типа, состоящее из одно из этих ключевых слов, за которым следует tag - специфицированный тип-спецификатор. Например, статус структуры и перечисление месяц являются спецификаторами специфицированных типов.

Таким образом, программа C, содержащая оба:

int status(); struct status;

ведет себя так же, когда компилируется как С++. Статус имени относится только к функция. Программа может ссылаться на введите только с помощью структура специфицированного типа-спецификатора статус.

Итак, как это сделать, чтобы ошибки ползли в программы? Рассмотрим программу в Листинг 1. Эта программа определяет class foo с конструктором по умолчанию, и оператор преобразования, который преобразует объект foo в char const *. Выражение

p = foo();

в main должен построить объект foo и примените оператор преобразования. последующий оператор вывода

cout << p << '\n';

должен отображать класс foo, но он не делает. Он отображает функцию foo.

Этот удивительный результат происходит потому, что программа включает в себя заголовок lib.h показано в листинг 2. Этот заголовок определяет функцию, также называемую foo. имя функции foo скрывает имя класса foo, поэтому ссылка на foo в main относится к функции, а не к классу. main может относиться к классу только посредством используя специфицированный тип-спецификатор, так как в

p = class foo();

Как избежать такой путаницы во всей программе - добавить после typedef для имени класса Foo:

typedef class foo foo;

непосредственно перед или после класса определение. Этот typedef вызывает конфликт между именем типа foo и имя функции foo (из библиотеки), которые ошибка времени компиляции.

Я не знаю никого, кто на самом деле пишет эти typedefs, как само собой разумеющееся. Это требует много дисциплины. поскольку частота ошибок, таких как один в Листинг 1, вероятно, довольно Маленькие, многие из вас никогда не сталкиваются с Эта проблема. Но если ошибка в вашем программное обеспечение может привести к телесным повреждениям, то вы должны написать typedefs no вопрос, насколько маловероятно ошибка.

Я не могу себе представить, почему кто-нибудь когда-либо хотите скрыть имя класса с помощью функции или имени объекта в том же как класс. Правила сокрытия в C были ошибкой, и они должны не были распространены на классы в С++. Действительно, вы можете исправить ошибка, но требует дополнительных дисциплина программирования и усилия, которые не требуется.

  • 9
    Если вы попробуете "class foo ()", и он потерпит неудачу: в ISO C ++ "class foo ()" является недопустимой конструкцией (кажется, статья была написана в '97, до стандартизации). Вы можете поставить "typedef class foo foo;" в main, тогда вы можете сказать "foo ();" (потому что тогда typedef-name лексически ближе, чем имя функции). Синтаксически, в T (), T должен быть спецификатором простого типа. подробные спецификаторы типов не допускаются. Тем не менее, это хороший ответ, конечно.
  • 0
    Ссылки на Listing 1 и Listing 2 работают. Посмотри.
Показать ещё 1 комментарий
60

Еще одно важное отличие: typedef не может быть объявлено вперед. Поэтому для параметра typedef вы должны #include файла, содержащего typedef, что означает все, что #include ваш .h также включает этот файл, независимо от того, нуждается он в этом или нет, и так далее. Это может определенно повлиять на время вашей сборки на более крупные проекты.

Без typedef в некоторых случаях вы можете просто добавить объявление вперед struct Foo; вверху вашего файла .h и только #include определение структуры в вашем файле .cpp.

  • 0
    Почему раскрытие определения структуры влияет на время сборки? Проводит ли компилятор дополнительную проверку, даже если в этом нет необходимости (учитывая опцию typedef, чтобы компилятор знал определение), когда он видит что-то вроде Foo * nextFoo; ?
  • 3
    Это на самом деле не дополнительная проверка, это просто больше кода, с которым должен иметь дело компилятор. Для каждого файла cpp, который встречает этот typedef где-то в его цепочке включений, он компилирует typedef. В более крупных проектах файл .h, содержащий typedef, может легко скомпилироваться сотни раз, хотя предварительно скомпилированные заголовки очень помогают. Если вы можете обойтись без использования предварительного объявления, тогда проще ограничить включение .h, содержащего полную спецификацию структуры, только тем кодом, который действительно заботится, и, следовательно, соответствующий включаемый файл компилируется реже.
Показать ещё 2 комментария
30

Там есть разница, но тонкая. Посмотрите на это следующим образом: struct Foo вводит новый тип. Второй создает псевдоним Foo (а не новый тип) для неназванного типа struct.

7.1.3 Спецификатор typedef

1 [...]

Имя, объявленное с помощью спецификатора typedef, становится typedef-name. В рамках своей декларации typedef-name синтаксически эквивалентно ключевому слову и называет тип, связанный с идентификатором в способ, описанный в разделе 8. Таким образом, typedef-name является синонимом другого типа. Имя typedef не вводит новый тип, как это делает объявление класса (9.1) или объявление перечисления.

8 Если декларация typedef определяет неназванный класс (или перечисление), первое имя typedef, объявленное объявлением для этого типа класса (или типа перечисления) используется для обозначения типа класса (или типа перечисления) для привязки только для целей (3.5). [Пример:

typedef struct { } *ps, S; // S is the class name for linkage purposes

Таким образом, typedef всегда используется в качестве заполнителя/синонима для другого типа.

10

Вы не можете использовать форвардное объявление с помощью структуры typedef.

Сама структура является анонимным типом, поэтому у вас нет фактического имени для переадресации объявления.

typedef struct{
    int one;
    int two;
}myStruct;

Прямое объявление вроде этого не работает:

struct myStruct; //forward declaration fails

void blah(myStruct* pStruct);

//error C2371: 'myStruct' : redefinition; different basic types
  • 0
    Я не получаю вторую ошибку для прототипа функции. Почему говорится «переопределение; разные основные типы»? компилятору не нужно знать, как выглядит определение myStruct, верно? Независимо от того, взята ли часть кода (typedef или предварительное объявление), myStruct обозначает тип struct, верно?
  • 0
    @Rich Жалуется на конфликт имен. Есть предварительное объявление, говорящее «ищите структуру с именем myStruct», а затем есть typedef, который переименовывает безымянную структуру в «myStruct».
Показать ещё 4 комментария
0

Важное различие между "typedef struct" и "struct" в С++ заключается в том, что инициализация членского членства в "typedef structs" не будет работать.

// the 'x' in this struct will NOT be initialised to zero
typedef struct { int x = 0; } Foo;

// the 'x' in this struct WILL be initialised to zero
struct Foo { int x = 0; };
  • 2
    Не правда. В обоих случаях x инициализируется. См. Тест в онлайн-среде разработки Coliru (я инициализировал его как 42, так что это более очевидно, чем с нулем, что назначение действительно имело место).
  • 0
    На самом деле, я тестировал его в Visual Studio 2013, и он не был инициализирован. Это была проблема, с которой мы столкнулись в производственном коде. Все компиляторы разные и должны соответствовать только определенным критериям.
-1

Struct - создать тип данных. Typedef - установить псевдоним для типа данных.

  • 6
    Вы не поняли вопрос.
-3

В С++ нет никакой разницы, но я верю в C, что позволит вам явно объявлять экземпляры структуры Foo:

struct Foo bar;
  • 3
    Посмотрите на ответ @ dirkgently - разница есть, но она неуловима.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню