
lafore_robert_objectoriented_programming_in_c
.pdf

Chapter 13
636
FIGURE 13.1
Files in a multifile application.
It is also often the case that a program is divided into separate files according to functionality: One file can handle the code involved in a graphics display, for example, while another file handles mathematical analysis, and a third handles disk I/O. In large programs, a single file may simply become too large to handle conveniently.
The techniques used for working with multifile programs are similar, whatever the reasons for dividing the program.


Chapter 13
638
Only a single command needs to be given to the compiler to compile all the source (.CPP and
.H) files and link the resulting .OBJ files (and any other .OBJ or .LIB files) into a final .EXE file. This is called the build process. Often the .EXE file can be executed as well. (In Windows and other advanced programming there are many more types of files.)
One of the nice things about a project is that it keeps track of the dates when you compiled each source file. Only those source files that have been modified since the last build are recompiled; this can save considerable time, especially on large projects. Some compilers distinguish between a Make command and a Build command. Make compiles only those source files that have changed since the last build, whereas Build compiles all files regardless of date.
Inter-File Communication
In a multifile program, program elements in different files need to communicate with each other. In this section we’ll discuss how to make this possible. We’ll first discuss how communication is handled between separately-compiled source (.CPP) files that are linked together. Then we’ll see how header (.H) files that are included in source files fit into the picture.
Communication Among Source Files
This section explores how elements of separate source files communicate. We’ll examine three kinds of programming elements: variables, functions, and classes. Each has its own rules for inter-file use.
The idea of scope will be important here, so you may want to refer back to our discussion of scope and storage class in Chapter 5. Scope is the region of a program where a variable or other program element can be accessed. Elements declared within a function have local scope; that is, they are visible only within the function body. Similarly, class members are only visible within the class (unless the scope resolution operator is used).
Program elements declared outside any function or class have global scope: they can be used throughout an entire file, following the point where they are defined. As we’ll see, they are visible in other files as well.
Inter-File Variables
We’ll start with simple variables. Recall the distinction between declaration and definition. We declare a simple variable by giving it a name and a type. This does not necessarily provide a physical location in memory for the variable; it only tells the compiler that a variable with this name and type may exist somewhere. A variable is defined when it is given a place in memory that can hold the variable’s value. The definition creates the “real” variable.


Chapter 13
640
You should note a possibly surprising restriction: you can’t initialize a variable in an extern declaration. The statement
extern int globalVar = 27; //not what you might think
will cause the compiler to assume that you meant to define globalVar, not just declare it. It will simply ignore the extern keyword and create a definition. If the variable is defined in another file, you’ll get the “already defined” error from the linker.
What if you actually want to use global variables with the same name in different files? In that case you can define them using the static keyword. This restricts a variable’s visibility to the file where it’s defined. Other variables with the same name can be used in other files.
//file A |
|
|
|
|
|
|
|
static int globalVar; |
//definition; |
visible |
only |
in |
file |
A |
|
//file |
B |
|
|
|
|
|
|
static |
int globalVar; |
//definition; |
visible |
only |
in |
file |
B |
Although two variables with the same name are defined here, there is no conflict. Code in file A that refers to globalVar will access the variable in its file, and code in file B behaves likewise. Static variables are said to have internal linkage, while non-static global variables have external linkage. (As we’ll see later in this section, you can also use namespaces to restrict a variable’s scope to a single file.)
In a multifile program it’s a good idea to make global variables static whenever they are not accessed in other files. This prevents problems when the same name is used by mistake in another file. It also makes it clearer to someone looking at the listing that they don’t need to worry about the variable being accessed elsewhere.
Notice that the keyword static has several meanings, depending on whether it’s applied to a local or a global variable. We saw in Chapter 5, “Functions,” that when static modifies a local variable (one defined inside a function) it changes the variable’s lifetime from that of the function to that of the program but keeps its visibility restricted to the function. As we discussed in Chapter 6, “Objects and Classes,” a static class data member has the same value for all objects rather than a separate value for each object. However, for a global variable, static simply restricts its visibility to its own file.
A const variable that is defined in one file is normally not visible in other files. In this regard it’s like a static variable. However, you can cause a const variable to be visible in another file by using the extern keyword with both the definition and the declaration:
//file A |
|
|
extern const int conVar2 = 99; |
//definition |
|
//file |
B |
|
extern |
const int conVar2; |
//declaration |




644 |
Chapter 13 |
|
//fileA.cpp |
|
|
int gloVar; |
|
//variable definition |
int GloFunc(int n) |
//function definition |
|
{ return |
n; } |
|
//fileB.cpp |
|
|
#include “fileH.h” |
|
|
. . . |
|
|
gloVar = 5; |
|
//work with variable |
int gloVarB |
= gloFunc(gloVar); |
//work with function |
Beware: you can put declarations in a header file, but you can’t put variable or function definitions in a header file that will be shared by multiple source files (unless they’re static or const). If you do, the same definitions will then end up in two different source files and the linker will issue “multiply defined” errors.
A very common and indeed almost essential technique is to put a class definition in a header file that is included in every source file that needs it. This doesn’t cause the multiply-defined problem because a class definition does not set aside any memory; it’s only a specification.
//fileH.h |
|
class someClass |
//class definition |
{ |
|
private: |
|
int memVar; |
|
public: |
|
int memFunc(int, int); |
|
}; |
|
//fileA.cpp |
|
#include “fileH.h” |
|
int main() |
|
{ |
|
someClass obj1; |
//create an object |
int var1 = obj1.memFunc(2, 3); |
//work with object |
} |
|
//fileB.cpp |
|
#include “fileH.h” |
|
int func() |
|
{ |
|
someClass obj2; |
//create an object |
int var2 = obj2.memFunc(4, 5); |
//work with object |
} |
|