Мне было любопытно знать, как точно определение типа работает.
typedef struct example identifier;
identifier x;
В вышеупомянутом операторе 'идентификатор' просто заменен (somethings как строковая замена) с 'примером структуры' в коде? Если не, что определение типа делает здесь?
просветите!
Нет, это не замена строки - это были бы макросы. Он создает псевдоним для типа.
typedefs предпочтительнее макросов для пользовательских типов, отчасти потому, что они могут правильно кодировать типы указателей.
typedef char *String_t;
#define String_d char *
String_t s1, s2;
String_d s3, s4;
s1, s2 и s3 объявлены как char *, но s4 объявлен как char, что, вероятно, не является намерением.
Все согласны с тем, что это не подстановка типов, и что это гораздо лучше, чем подстановка типов, когда в дело вступают указатели, но есть и другие тонкости. В частности, использование типизаторов может повлиять на разбор кода и на валидность программ.
И в C, и в C++ идентификаторы, определяемые пользователем, хранятся в разных пространствах имен (не в смысле C++, а в смысле идентификаторов). Когда вы используете ключевое слово typedef, вы создаете псевдоним для типа в глобальном пространстве имен, где находятся функции.
// C/C++
struct test {};
void test( struct test x ) {} // ok, no collision
// C/C++
typedef struct test {} test; // test is now both in types and global spaces
//void test() {} // compiler error: test is a typedef, cannot be redefined
Небольшое отличие здесь в том, что в C++ компилятор сначала будет искать в глобальном пространстве имен, а если не найдет там, то будет искать и в пространстве имен типов:
// C
struct test {};
//void f( test t ); // error, test is not defined in the global namespace
void f( struct test t ); // ok, we are telling the compiler where to look
// C++
struct test {};
void f( test t ); // ok, no test defined in the global name space,
// the compiler looks in the types name space
void g( struct test t ); // also ok, even if 'struct' not needed here.
Но это не означает, что два пространства имен слиты, только то, что поиск будет вестись в обоих местах.
А typedef
вводит синоним для типов. Это не простая замена строки, как показано ниже:
typedef int* int_ptr;
const int* a; // pointer to const int
const int_ptr b; // const pointer to int
Компилятор также знает, что это имя типа, вы не можете просто поместить его куда-то, где тип не разрешен, не получив ошибку компилятора.
Одним из важных отличий является то, что typedef
имеют область видимости .
Ниже приводится общая идиома.
class Foo: public Bar
{
private:
typedef Bar inherited;
public:
Foo(int x) : inherited(x) {}; // preferred to 'Bar(x)'
}
Часто определение конструктора содержится в файле .cpp с объявлением в заголовке. Если вы используете Foo (int x): Bar (x)
, есть большая вероятность, что вы забудете обновить конструктор, если измените иерархию классов так, чтобы Foo-> Wibble-> Bar вместо Foo- > Бар. Лично я рекомендую добавлять «унаследованный» typedef к каждому подклассу.
Подробнее см. Здесь: Использование "super" в C ++
Макросы выполняются препроцессором и основаны исключительно на подстановке текста. Таким образом, можно сказать, что они довольно глупы. Препроцессор примет практически любой мусор без какой-либо проверки синтаксиса.
Typedefs выполняются самим компилятором и манипулируют собственной таблицей типов компилятора, добавляя производный тип, который вы определяете. Это подлежит полной синтаксической проверке, а также механизму, предназначенному только для типов.
Можно считать, что компилятор выполняет аналогичную "работу", как если бы вы объявили struct
. Там есть определение, и компилятор превращает его в тип в своем списке типов.
С помощью typedef вы создаете псевдоним. Компилятор заменяет псевдоним правильным кодом.
Если вы напишете:
typedef int company_id;
company_id mycompany = 100;
Компилятор получит:
int mycompany = 100;
Я считаю, что определения типов значительно упрощают чтение сигнатур функций. Предположим, вы хотите вернуть указатель на двумерный массив. Вот способ чтения:
typedef int three_by_three[3][3];
three_by_three* foo();
А вот как это можно сделать без typedef:
int (*bar())[3][3];
Обратите внимание, что эта подпись совсем не похожа на первую версию с примененной «заменой строки».
Если бы синтаксис декларатора C не был таким уродливым (Страуструп: «Я считаю синтаксис декларатора C неудачным экспериментом»), typedefs, вероятно, не использовались бы так часто, как сейчас.
Typedef - это ярлык, который создает новое имя для (обычно сложного) типа. Его назначение более узкое, чем замена строк в препроцессоре. Таким образом, он менее подвержен ошибкам, чем определения препроцессора (которые разбираются рекурсивно).