Multi-file programs consist of a combination of header (.h) and source code (.cpp) files. Header files are always #included in source code files, but #including one source code file into another is very rare. The linker (or loader) assembles the final program or executable from the machine code generated from the individual source code files. Please review:
This version of the Time example begins with and extends the Time example started in the previous chapter. It builds on passing arguments to functions. Specifically, the updated example illustrates pass-by-reference and pass-by-pointer by making the following changes to Time:
Modify the "print" function so that it performs a pass-by-reference
Add a read function that:
Has a return type of void
Has one Time argument implemented as pass-by-pointer
Prompts for and reads (three separate prompts and three separate reads) in data to fill the three Time fields in this order: hours, minutes, and seconds
The modified program's organization still has one header and two source code files. Adding a read function (requirement 2) and modifying the print function (requirement 1) involves editing the two "Time" files. Testing the changes requires editing the driver. The three-file organization and function patterns make it easy to convert the example to a class with member functions in a subsequent chapter.
Recall that overload functions must each have a unique argument list. While a program can have functions overloaded as pass-by-reference and pass-by-pointer, there is no practical value to including both in a "real" program because both implement INOUT passing. The example has both to illustrate their different syntaxes. Conversely, programs can't define functions overloaded as pass-by-value and pass-by-reference because the function calls for both are identical.
The Time Header File: Time.h
Header files typically contain declarations, implying they can be included in a program many times without causing conflicts or programming errors. A structure specification qualifies as a declaration because it doesn't create objects or allocate memory. Similarly, function prototypes qualify as declarations because they don't have bodies for the compiler to translate to machine code and don't use memory.
make_time: The Construction Functions
add: Process Function
read and print: Time I/O Functions
Testing Time: driver.cpp
In a bottom-up implementation, programmers use "drivers" to test functions. They frequently "hard code" argument values in the calls because the drivers don't solve "real" or application problems - they are just a sequence of test calls. Programmers often follow a cyclic development process of implementing and testing a function or a small set of functions. Cyclic development has at least two advantages:
It's easier to localize, identify, and correct errors when restricted to a small code region.
One function may depend on another. Correcting the independent function before implementing a dependent function reduces the need to correct or update dependent functions.
Following a cyclic process has advantages, but a functional "critical mass" is necessary for any testing. Specifically, the driver must build, populate (i.e., fill with data), and display objects. When a driver can perform these basic tasks, it can begin testing the (typically) more complex process functions.