zouyongbin said:
Stanley B Lippman in his "C++ Primer" that a definition like this
should not appear in a header file:
int ix;
That is a guideline, not a rule. Guidelines can be broken.
Firstly, a static definition can certainly appear in a header.
static int x = 3;
This will create a private x in every translation unit that includes
it.
Secondly, you can use careful #if or #ifdef guards to ensure that the
one definition rule is observed, even if you have external definitions
in header files.
This can be exploited as follows:
// A.h
#ifndef A_H_34DF_C91B
#define A_H_34DF_C91B
class A {
...
};
#ifdef GENERATE_DEFS
A global_instance_a;
#else
extern A global_instance_a;
#endif
#endif
#endif
// End of A.h
// B.h
#ifndef B_H_37D0_1031
#define B_H_37D0_1031
#include "A.h"
class B {
... uses class A in some way
};
#ifdef GENERATE_DEFS
B global_instance_b(global_instance_a);
#else
extern B global_instance_b;
#endif
// End of B.h
Here we have two headers, A.h and B.h. They respectively define classes
A and B. Normally, the GENERATE_DEFS symbol is not defined and so the
headers provide an external declaration for objects global_instance_a
and global_instance_b.
The global_instance_b object knows about global_instance_a; it uses it,
and therefore global_instance_a must be initialized before
global_instance_b.
You create some source file where you do this:
#define GENERATE_DEFS
#include "B.h"
Now this becomes a translation unit in which all of the dependent
definitions appear, and in a sequence which ensures their correct
initialization order.
But as we know, the definition of a class is always in a
header file.
No, that is a class declaration, not a definition.
And we can use "#ifndef" to eliminate the linker error
complaining about multiple definitions.
No, the #ifdef eliminates multiple declarations of the same class in
the same translation unit. It does not prevent different translation
units from including that class.
This is different from the restriction on an external definition, which
must be made in exactly one place in the entire program.
The way many C++ implementations work internally, a duplicate class
declaration in the same translation unit is caught and diagnosed by the
compiler. Duplicate external definitions made in separate translation
units are caught by the linker.
You can actually have multiple declarations of an object in the same
(file) scope, but only one definition. E.g.
int x;
extern int x;
int x;
int x = 3;
This is a valid translation unit.
You can also have multiple declarations of a class, albeit only one of
them can provide a body, e.g:
class A;
class A {
...
};