Разница между «struct» и «typedef struct» в C ++? - PullRequest
771 голосов
/ 04 марта 2009

В C ++ есть ли разница между:

struct Foo { ... };

и

typedef struct { ... } Foo;

Ответы [ 8 ]

1109 голосов
/ 04 марта 2009

В 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 'ed, если имя не скрыто другим объявлением с таким же именем. См. Ответ Майкла Барра для получения полной информации.

216 голосов
/ 05 марта 2009

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

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

typedef class string string;

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

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

int status(); struct status;

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

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

typedef struct status status;

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

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

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

int status(); struct status;

ведет себя так же при компиляции как C ++. Одно только название статуса относится к функция. Программа может относиться к введите только с помощью сложная структура спецификатора типа состояние.

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

p = foo();

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

cout << p << '\n';

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

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

p = class foo();

Способ избежать такой путаницы на протяжении всей программы, чтобы добавить следующий typedef для имени класса Foo:

typedef class foo foo;

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

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

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

62 голосов
/ 04 марта 2009

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

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

31 голосов
/ 04 марта 2009

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

7.1.3 Спецификатор определения типа

1 [...]

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

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

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

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

10 голосов
/ 05 августа 2013

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

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

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

Такое предварительное объявление не будет работать:

struct myStruct; //forward declaration fails

void blah(myStruct* pStruct);

//error C2371: 'myStruct' : redefinition; different basic types
1 голос
/ 08 апреля 2017

Структура заключается в создании типа данных. Typedef - установить псевдоним для типа данных.

0 голосов
/ 11 апреля 2016

Важное различие между 'typedef struct' и 'struct' в C ++ заключается в том, что инициализация встроенного члена в '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; };
0 голосов
/ 04 марта 2009

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

struct Foo bar;
...