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

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

struct Foo { ... };

и

typedef struct { ... } Foo;

8 ответов


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

стандарт языка C (C89 §3.1.2.3, C99 §6.2.3 и C11 §6.2.3) мандаты отдельных пространств имен для различных категорий идентификаторов, в том числе тег идентификаторы (for struct/union/enum) и обычные идентификаторы (for 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, пока имя не скрыто другой декларацией с тем же именем. См.ответ Майкла Берра для полной информации.


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

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

typedef class string string;

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

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

int status(); struct status;

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

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

typedef struct status status;

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

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

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

int status(); struct status;

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

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

p = foo();

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

cout << p << '\n';

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

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

p = class foo();

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

typedef class foo foo;

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

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

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


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

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


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

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

1 [...]

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

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

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

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


вы не можете использовать прямое объявление с typedef struct.

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

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

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

struct myStruct; //forward declaration fails

void blah(myStruct* pStruct);

//error C2371: 'myStruct' : redefinition; different basic types

важное различие между "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; };

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


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

struct Foo bar;