How do I prevent header files from being duplicated or referenced?

Source: Internet
Author: User

first, #pragma once (more commonly used)

Adding this instruction at the beginning of the header file ensures that the header file is compiled once, and this instruction is actually available in the VC6, but there is not much use for compatibility.

#pragmaonce是编译相关, which means it can be used on this compilation system, but not necessarily in other compiling systems, that is, poor portability, but now it's basically a definition for every compiler.

#pragmaonce这种方式, is unique to the Microsoft compiler, but also later, so know not many people, the use of people is not many, because he does not support Cross-platform. If you want to write Cross-platform code, it's best to use the previous one. This is a way that is supported by the compiler to prevent two compilation of the same file, where the same file refers to the physical file.

He also has the disadvantage:

If you have multiple copies of a header file, these files are logically the same, but they are different physically, so when you include these files, you'll find that they're actually included, and then it's a compile error. Also, when the same physical file is nested, using the first method preprocessing will open the file every time to make a decision, but the second method will not, so this #pragma once will be faster. The following examples illustrate

Test1.h
#ifndefine Test1_h
#defineTEST1_H
...
#endif

Test2.h
#pragma once
...

Test.cpp
#include "Test1.h"//Line 1
#include "Test1.h"//Line 2
#include "Test2.h"//Line 3
#include "Test2.h"//Line 4 The Test2.h here is the same physical file

Preprocessor in the execution of these four sentences, first open Test1.h and then found that the inside of the macro test1_h is not defined, so will contain the file, the second sentence, the same will open the Test2.h found that the macro has been defined, does not include the file pressed. In the third sentence, when the discovery does not contain the test2,h will be included in the file, the execution of the fourth sentence, found that the file has been included, so do not have to open to skip directly

Second, conditional compilation

#include "A.h"

#include "B.h"

It doesn't seem to be a problem. If A.h and B.h both contain a header file x.h. So X.h is also included in this two times, but its form is not so obvious.

Multiple inclusions In most cases appear in large programs, it often requires a lot of headers, so it's not easy to find duplicate inclusions. To solve this problem, we can use conditional compilation. If all of the header files are written as follows:
#ifndef _headername_h
#define _headername_h

...//(header file content)

#endif

Then the risk of multiple inclusions is eliminated. When the head file is first included, it is processed normally, and the symbol HEADERNAME_H is defined as 1. If the header file is included again, the content is ignored by conditional compilation. The symbol Headername_h is named according to the file name of the included header file to avoid conflicts caused by the use of the same symbols by other header files.

However, you must remember that the preprocessor still reads the entire header file, even if all the contents of the header file are ignored. Because this processing will slow down the compilation speed, you should avoid multiple inclusions if possible.

Problem: test-1.0 using #ifndef just prevents the header file from being duplicated (in fact, there is only one header in this case, there is no duplicate problem), but it cannot prevent the variable from being repeatedly defined. such as the following code:

VS 2012:test.c


#include <stdio.h>
#include "test.h"

extern i;
extern void Test1 ();
extern void Test2 ();

int main ()
{
Test1 ();
printf ("ok/n");
Test2 ();
printf ("%d/n", I);
return 0;
}



VS 2012:test.h


#ifndef _test_h_
#define _test_h_

Char add1[] = "www.shellbox.cn/n";
Char add2[] = "www.scriptbox.cn/n";
int i = 10;
void Test1 ();
void Test2 ();

#endif




VS 2012:test1.c

--
#include <stdio.h>
#include "test.h"

extern Char add1[];

void Test1 ()
{
printf (ADD1);
}




VS 2012:test2.c

#include <stdio.h>
#include "test.h"

extern Char add2[];
extern i;

void Test2 ()
{
printf (ADD2);
for (; i > 0; i--)
printf ("%d-", I);
}

Error Analysis:
Because every. c file in the project is interpreted independently, even if the header file has
#ifndef _test_h_ #define _test_h_ ... #enfif
In other files, the test.h is interpreted as long as it is contained, and then each. c file generates a separate identifier. When the compiler links, all the symbols in the project are integrated together, because the file has the duplicate name variable, so there is a duplicate definition of the error.

Workaround:
Define the variables in the. c file, and then build a header file (. h file), plus extern before all the variable declarations, and note that you do not initialize the variables here. The. h file is then included in other. c files that need to use global variables. The compiler generates the destination file for. C, and when linked, if the. c file uses a global variable, the linker is linked to the. c file that defines the variable.

VS 2012:test.h

//-------------------------------

#ifndef _test_h_

#define _test_h_

extern int i;

extern Char add1[];

extern Char add2[];

void Test1 ();

void Test2 ();

#endif

VS 2012:test.c

//-------------------------------

#include <stdio.h>

#include "test.h"

int i = 10;

Char add1[] = "www.shellbox.cn/n";

Char add2[] = "www.scriptbox.cn/n";

extern void Test1 ();

extern void Test2 ();

int main ()

{

Test1 ();

printf ("ok/n");

Test2 ();

printf ("%d/n", I);

return 0;

}

VS 2012:test1.c

//-------------------------------

#include <stdio.h>

#include "test.h"

extern Char add1[];

void Test1 ()

{

printf (ADD1);

}

VS 2012:test2.c

//-------------------------------

#include <stdio.h>

#include "test.h"

extern Char add2[];

extern int i;

void Test2 ()

{

printf (ADD2);

for (; i > 0;i--)

printf ("%d-", I);

}

Problem extension: The declaration of a variable has two things:

(1) One is the need to establish storage space (definition, declaration). For example, int A has established storage space when it is declared.
(2) The other is that there is no need to create storage space (declaration). For example: extern int A, where variable A is defined in a different file.
The former is a "defined declaration (defining declaration)" or "definition", and the latter is a "referential declaration (referncingdeclaration)". In a broad sense, a declaration contains a definition, but not all declarations are defined, for example: int A is both a declaration and a definition. For extern A, however, it is merely a declaration and not a definition. In general, we often describe the creation of space as a "definition", and call it "declaration" without the need to create storage space. It is clear that the statement we are referring to here is narrower, that is to say, a declaration of a undefined nature.

For example: in the main function
int main ()
{
extern int A; This is a declaration rather than a definition, declaring a is an external variable that has already been defined
Note: When declaring an external variable, you can remove the variable type as: extern A;
Dosth (); Execution function
}

int A; is defined as an external variable (global variable) that defines a as an integral type


The "definition" of an external variable (global variable) is not the same as the "declaration" of an external variable, an external variable can be defined only once, its position is outside all functions, and an external variable declaration in the same file can be multiple times, and it can be within a function (which function is declared in that function). It can also be outside of a function (before the definition point of an external variable). The system allocates storage based on the definition of an external variable, rather than on the declaration of an external variable. For external variables, initialization can only be done in the definition, not in the declaration. The function of a so-called "declaration" is to declare that the variable is an external variable that has been defined later, merely as a "declaration" for referring to the variable "in advance". extern only makes a declaration and does not define it.

The function of declaring a variable with static is two:
(1) with a static declaration for a local variable, the space allocated for the variable always exists throughout the execution of the program
(2) If the external variable is declared with static, the function of the variable is limited to this file module


(This section is referenced from: How to prevent header files from being repeatedly included, nested included)

third, the predecessor statement:

When writing a C + + program, you occasionally need to use a predecessor statement (Forward Declaration). In the following program, the line with the annotation is the predecessor description of Class B. This is necessary, because Class A uses the type B,

The Declaration of Class B appears in the back of Class A. If you do not have a previous description of Class B, the following program will be different by compiling, and the compiler will give an error message similar to "Missing type descriptor".

A.h

#include "B.h"

Class A

{

b b;

Public

A (void);

Virtual ~a (void);

};

A.cpp

#include "A.h"

A::a (void)

{

}

A::~a (void)

{

}

B.h

#include "A.h"

Class B

{

A A;

Public

B (void);

~b (void);

};

B.cpp

#include "B.h"

B::b (void)

{

}

B::~b (void)

{

}



Compile the A.cpp and do not pass. Then compile the B.cpp, or do not pass. compiler to compile A.h, found that contains B.h, to compile B.h. Compile B.h When the discovery contains A.h, but A.h has been compiled (in fact, there is no compilation completed, the compiler may have done a record, A.h has been compiled, so as to avoid falling into a dead loop. Compilation errors are always stronger than the dead loop, and you will continue compiling without recompiling A.h. Later found that the definition of a, which is good, the definition of A is not compiled, so the definition of a is not found, the compilation error.

Using a predecessor statement can then solve the problem:

A.h

#include "B.h"

Class B; Predecessor declaration

Class A

{



Private

b b;

Public

A (void);

Virtual ~a (void);

};

A.cpp

#include "A.h"

A::a (void)

{

}

A::~a (void)

{

}

B.h

#include "A.h"

Class B

{

Private

A A;

Public

B (void);

~b (void);

};

B.cpp

#include "B.h"

B::b (void)

{

}

B::~b (void)

{

}


Test.cpp


int main ()

{

b* B = new B ();

A * a = new A ();

Delete A;

Delete B;

return 0;

}

There are many benefits to the predecessor declaration of a class.

One advantage of our use of the predecessor statement is that from the above, when we modify Class B in Class A using the predecessor Declaration of Class B, we only need to recompile class B without recompiling the A.H (of course, you must include b.h when you actually use Class B).

Another advantage is to reduce the size of Class A, the above code is not reflected, then we look at:

A.h

Class B;

Class A

{

....

Private

B *b;

....

};

B.h

Class B

{

....

Private

int A;

int b;

int C;

};

We look at the code above, the size of Class B is 12 (on a 32-bit machine).

If we include object B in Class A, the size of Class A is 12 (assuming there are no other member variables and virtual functions). If you include the pointer *b variable for Class B, then Class A is 4, so you can reduce the size of Class A,

This is especially useful when the STL container contains a class object rather than a pointer. In the predecessor declaration, we can only use the pointer and reference of the class (because the reference is also the implementation of the pointer).

Why do we have to use only the type of pointers and references when we put forward a declaration?

Look at the following class:

Class A

{

Public

A (int a): _a (a), _b (_a) {}//_b is new add

int get_a () const {return _a}

int Get_b () const {return _b;}//New add

Private

int _b; New add

int _a;

};

This class A, defined above, where the _b variable and the Get_b () function are added to this class.

Change:

The first change is of course the addition of the _b variable and the Get_b () member function;

The second change is that the size of the class changes, it turns out to be 4, and now it's 8.

The third change is that the offset address of the member _a is changed, and the original offset from the class is 0, now 4.

The changes above are our explicit and visible changes. There's also a hidden change.

The hidden change is that the default constructor for Class A and the default copy constructor change.

As you can see from the above changes, the behavior of any member variable or member function that invokes Class A needs to be changed, so our a.h need to be recompiled.

If our b.h is like this:

B.h

#include "A.h"

Class B

{

...

Private

A A;

};

Then our b.h also need to be recompiled.

If this is the case:

B.h

class A

class B

{

...

Private:

A *a;

};

Then our b.h will not need to be recompiled.

Like we do the predecessor declaration Class A:

ClassA;

is an incomplete declaration, as long as there is no execution in class B that needs to know the size of Class A or the operation of a member, such an incomplete declaration allows a declaration to point to a pointer and a reference.

And the statement in the previous code

Aa;

It is necessary to understand the size of a, otherwise it is impossible to know if the memory size is allocated to Class B, the incomplete predecessor declaration will not work, you must include A.H to get the size of Class A, and also recompile class B.

To go back to the previous question, the use of a predecessor declaration to allow only declarations that are pointers or references is one reason that as long as the declaration is not executed it is necessary to understand the size of class A or the operation of the member, so declaring a pointer or reference is not

execution needs to know the size of Class A or the operation of a member

a predecessor statement resolves the interdependence of two classes

A.h

Class B;

Class A

{

b* b;

Public

A (b* B): b (b)

{}

void Something ()

{

B->something ();

}

};

A.cpp

#include "B.h"

#include "A.h"

A::a (b * b)

{

b= new B;

}

A::~a (void)

{

Delete B;

}

B.h

Class A;

Class B

{

A A;

Public

B (void);

void Something ()

{

cout<< "Something happend ..." <<endl;

}

~b (void);

};

B.cpp

#include "A.h"

#include "B.h"

B::b (void)

{

A= New A;

}

B::~b (void)

{

}


Test.cpp


int main ()

{

b * n = new B ();

A *a = new A (b);


Delete A;

Delete B;

return 0;

}


Error found after compilation: undefined type B was used;

The left side of the->something must point to class/struct/union/Type

Reason:

1. (1) The definition of type B is used because a member function in Class B is called. The predecessor declaration class B; only declares a type with a B, and does not give the relevant definition, the related definition of Class B, which appears after Class A, so there is a compilation error;

2. Code one is able to compile because it only uses the type B and does not use the definition of Class B.

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.