I was curious to know how exactly typedef works.
typedef struct example identifier;
identifier x;
In above statement is 'identifier' just replaced (somethings like string replacement) with 'struct example' in code? If no, what does typed开发者_Python百科ef do here?
please enlighten!
No, it is not a string replacement - that would be macros. It creates an alias for the type.
typedefs are preferred over macros for custom types, in part because they can correctly encode pointer types.
typedef char *String_t;
#define String_d char *
String_t s1, s2;
String_d s3, s4;
s1, s2, and s3 are all declared as char *, but s4 is declared as a char, which is probably not the intention.
A typedef
introduces a synonym for types. It isn't plain string replacement, as the following shows:
typedef int* int_ptr;
const int* a; // pointer to const int
const int_ptr b; // const pointer to int
The compiler also is aware that it is a type name, you can't just put it somewhere where a type isn't allowed without getting a compiler error.
All agree in that it is not type substitution, and that it is much better than it when pointers get into the mix, but there are also other subtleties. In particular the use of typedefs can affect how code is parsed and the validity of programs.
Both in C and in C++ user defined identifiers are kept in different name spaces (not in the C++ sense, but some kind of identifier-space). When you use the typedef keyword you make an alias for the type in the global name space, where functions reside.
// 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
A slight difference here is that in C++ the compiler will first look in the global name space and if not found there it will also look in the types name space:
// 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.
But this does not mean that the two namespaces are merged, only that the lookup will search in both places.
One important distinction is that typedef
s have scope.
The following is a common idiom
class Foo: public Bar
{
private:
typedef Bar inherited;
public:
Foo(int x) : inherited(x) {}; // preferred to 'Bar(x)'
}
Often you would have the constructor definition in a .cpp file, with the declaration in header. If you use Foo(int x) : Bar(x)
, there's a good chance you will forget to update the constructor if you change the class hierarchy such that Foo->Wibble->Bar instead of Foo->Bar. Personally I recommend adding the 'inherited' typedef to every subclass.
See here for more details: Using "super" in C++
Typedef is a shortcut which creates new name for (usually complex) type. It's purpose is more narrow than preprocessor's string replacement. Thus, it is less error-prone than preprocessor definitions (which are parsed recursively).
With typedef you make an alias. The compiler replaces the alias with the correct code.
If you write:
typedef int company_id;
company_id mycompany = 100;
The compilers gets:
int mycompany = 100;
Macros are done by the preprocessor and are purely based on text substitution. Thus, you could say they're pretty dumb. The preprocessor will accept pretty much any garbage without any syntax check.
Typedefs are done by the compiler itself and they manipulate the compiler's own type table by adding a derived type you define. This is subject to full syntax checking, and a mechanism specifically for types only.
You could think of the compiler doing similar "work" to when you declare a struct
. There's a definition there, and the compiler turns it into a type in its list of types.
I find typedefs make function signatures a lot easier to read. Suppose you want to return a pointer to a two-dimensional array. Here is the readable way:
typedef int three_by_three[3][3];
three_by_three* foo();
And here is how you can do it without a typedef:
int (*bar())[3][3];
Note that this signature does does not look at all like the first version with a "string replacement" applied.
If the C declarator syntax wasn't that ugly (Stroustrup: "I consider the C declarator syntax an experiment that failed"), typedefs probably wouldn't by used as much as they are now.
精彩评论