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:
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.
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.
struct Time { int hours; int minutes; int seconds; }; Time make_time(int h, int m, int s); Time make_time(int s); Time add(Time t1, Time t2); //void print(Time t);// (a) pass-by-value (or pass-by-copy) void print(Time& t); // (b) pass-by-reference void read(Time* t); // (c) pass-by-pointer // alternate versions void print(Time* t); // (d) pass-by-pointer void read(Time& t); // (e) pass-by-reference
#include <iostream> #include <iomanip> #include "Time.h" using namespace std; Time make_time(int h, int m, int s) // (a) { Time temp; temp.hours = h; temp.minutes = m; temp.seconds = s; return temp; } Time make_time(int s) // (b) { Time temp; temp.hours = s / 3600; s %= 3600; // shortcut for s = s % 3600; temp.minutes = s / 60; temp.seconds = s % 60; return temp; }
Time add(Time t1, Time t2) { int i1 = t1.hours * 3600 + t1.minutes * 60 + t1.seconds; int i2 = t2.hours * 3600 + t2.minutes * 60 + t2.seconds; return make_time(i1 + i2); }
//void print(Time t)// (a) void print(Time& t) // (b) { cout.fill('0'); cout << t.hours << ":" << setw(2) << t.minutes << ":" << setw(2) << t.seconds << endl; cout.fill(' '); } void read(Time* t) // (c) { cout << "Please enter the hours: "; cin >> t->hours; cout << "Please enter the minutes: "; cin >> t->minutes; cout << "Please enter the seconds: "; cin >> t->seconds; } // Alternate implementations void print(Time* t) // (d) { cout.fill('0'); cout << t->hours << ":" << setw(2) << t->minutes << ":" << setw(2) << t->seconds << endl; cout.fill(' '); } void read(Time& t) // (e) { cout << "Please enter the hours: "; cin >> t.hours; cout << "Please enter the minutes: "; cin >> t.minutes; cout << "Please enter the seconds: "; cin >> t.seconds; }
fill
and setw
make the hours and minutes display as two digits; the leading digit is 0 if the values are less than 10In 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:
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.
#include <iostream> #include "Time.h" using namespace std; int main() {Time t = make_time(3666);Time t; read(&t); print(t);Time s = make_time(1, 30, 4);Time s; read(&s); print(s); Time u = add(t, s); // adds two Time structs, stores the sum in u print (u); // prints the sum return 0; }