• No se han encontrado resultados

So far we have defined a number of abstractions (functions and a data structure) that are useful in solving our various grading problems. The only way we have seen of using these abstractions is to put all their definitions in a single file and compile that file. Obviously, this approach becomes complicated very quickly. To reduce that complexity, C++, like many languages, supports the notion of separate compilation, which allows us to put our program into separate files and to compile each of these files independently.

We'll start by understanding how to package the median function so that others can use it. We begin by putting the definition of the median function into a file by itself so that we can compile it separately. That file must include declarations for all the names that the median function uses. From the library, median uses the vector type, the sort function, and the domain_error exception, so we will have to include the appropriate headers for these facilities:

// source file for the median function

#include <algorithm>> // to get the declaration of sort

#include <stdexcept> // to get the declaration of domain_error #include <vector> // to get the declaration of vector

using std::domain_error; using std::sort; using std::vector; // compute the median of a vector<double>

// note that calling this function copies the entire argument vector double median(vector<double> vec)

{

// function body as defined in §4.1.1/53 }

As with any file, we must give our source file a name. The C++ standard does not tell us how to name source files, but in general, a source file's name should suggest its contents. Moreover, most implementations constrain source-file names, usually requiring the last few characters of the name to have a specific form. Implementations use these file suffixes to determine whether a file is a C++ source file. Most implementations require the names of C++ source files to end in .cpp, .C, or .c, so we might put the median function in a file named median.cpp, median.C, or median.c, depending on the implementation.

The next step is to make the median function available to other users. Analogous with the standard library, which puts the names it defines into headers, we can write our own header file that will allow users to access names that we define. For example, we could note in a file named median.h that our median function exists. If we did so, users could use it by writing:

// a much better way to use median #include "median.h"

#include <vector> int main(){/*...*/}

When we use a #include directive with double quotes rather than angle brackets, surrounding the header name, we are saying that we want the compiler to copy the entire contents of the header file that corresponds to that name into our program in place of the #include directive. Each implementation decides where to look for header files, and what the relationship is between the string between the quotes and the name of the file. We shall talk about "the header file median.h" as shorthand for "the file that the implementation decides is the one that corresponds to the name median.h." It is worth noting that although we refer to our own headers as header files, we refer to the implementation-supplied headers as standard headers rather than standard header files. The reason is that header files are genuine files in every

C++ implementation, but system headers need not be implemented as files. Even though the #include directive is used to access both header files and system headers, there is no requirement that they be implemented in the same way. Now that we know we must supply a header file, the obvious question is what should be in it. The simple answer is that we must write a declaration for the median function, which we do by replacing the function body with a

semicolon. We can also eliminate the names of the parameters, because they are irrelevant without the function body:

double median(vector<double>);

Our median.h header cannot contain just this declaration; we must also include any names that the declaration itself uses. This declaration uses vector, so we must make sure that that name is available before the compiler sees our declaration:

// median.h

#include <vector>

double median(std::vector<double>);

We include the vector header so that we can use the name std::vector in declaring the argument to median. The reason that we mention std::vector explicitly, rather than writing a using-declaration, is more subtle.

In general, header files should declare only the names that are necessary. By restricting the names contained in a header file, we can preserve maximum flexibility for our users. For example, we use the qualified name for std::vector because we have no way of knowing how the user of our median function wants to refer to std::vector. Users of our code might not want a using-declaration for vector. If we write one in our header, then all programs that include our header get a using std::vector declaration, regardless of whether they wanted it. Header files should use fully qualified names rather than using-declarations.

There is one last detail to cover: Header files should ensure that it is safe to include the file more than once as part of compiling the program. As it happens, our header file is already safe as it stands, because it contains only declarations. However, we consider it good practice to cater to multiple inclusion in every header file, not just the ones that need it. We do so by adding some preprocessor magic to the file:

#ifndef GUARD_median_h #define GUARD_median_h // median.h—final version #include <vector> double median(std::vector<double>); #endif

The #ifndef directive checks whether GUARD_median_h is defined. This is the name of a preprocessor variable, which is one of a variety of ways of controlling how a program is compiled. A full discussion of the preprocessor is beyond the scope of this book.

In this context, the #ifndef directive asks the preprocessor to process everything between it and the next matching #endif if the given name is not defined. We must choose a name that is unique in the entire program, so we make one from the name of our file and a string, such as GUARD_, that we hope will not be duplicated elsewhere.

The first time median.h is included in a program, GUARD_median_h will be undefined, so the preprocessor will look at the rest of the file. The first thing it does is to define GUARD_median_h, so that subsequent attempts to include median.h will have no effect.

comment before it:

#ifndef variable ...

#endif

The reason is that some C++ implementations detect files that have this form and, if the variable is defined, do not even bother to read the file the second time around.