Academic Integrity: tutoring, explanations, and feedback — we don’t complete graded work or submit on a student’s behalf.

Programming Strategies After you create your class and struct definitions in the

ID: 3860361 • Letter: P

Question

Programming Strategies

After you create your class and struct definitions in the header file, create a separate file called exerciseImplemenation.cpp or implementation.cpp, or some other descriptive name. Place all of the member functions in this file. Then create another source code file called exerciseMain.cpp, or just main.cpp. Place the main function in this file. You will have to include your header file in both of the source code files in order to get the project to compile correctly. Suppose the name of your header file is exerciseHeader.hpp (or you can use the extension .h). Then to include the header file in both source code files, use:

#include “exerciseHeader.hpp” // (or “exerciseHeader.h”)

at the top of both source code files. Notice the double quotes there instead of angled brackets (<>). The double quotes direct g++ to look inside of the current directory to find the header file. Remember that because the header file is included in the source code files, it is not necessary to list the header file on the command line during compilation. So, to compile your project, it may look like this:

g++ main.cpp implementation.cpp –o exerciseProgram

Remember that headers for member functions have to include the name of the class and the scope resolution operator. So the loadData() method header might look like:

int exerciseJournal::loadData()

Remember that all class members are accessible inside of member functions. So even though countAndIndex is private, it’s not private inside of loadData(), because loadData() is a member function. So inside of your member functions, you can access all private members directly. Inside of loadData(), set countAndIndex equal to zero, and increment it when you load an exercise, just like normal: countAndIndex = 0; countAndIndex++; Notice that loadData() still returns a value, so you can say how many exercises were loaded when control returns back to main() (don’t do any output inside of loadData; no cout statements there). So loadData() should return countAndIndex.

Once you have created your exerciseJournal class and implementation, you will have to instantiate it, or create an exerciseJournal object, in the main function. Then you will use the object to call the member functions. See the textbox to the right and above for an example.

Notice that I have placed the fileName c-string inside of the class, along with the countAndIndex member variable. I did this so that the file name will be accessible to all of the member functions that need it, such as loadData() and writeData(). Since the file name is part of the class, it won’t have to be passed as an argument. You may add any other member variables to the class that you feel you need, such as the fstream object, other c-strings, counting variables, or whatever, but make sure you add them as private members. You may also create other member functions if you find that you need them. For example, if you wish to have access to the countAndIndex value in main, you will need a “getter” (accessor) function for it. So the prototype (inside of the class) would be: int getCountAndIndex(); and the header would be:

int exerciseJournal::getCountAndIndex()

Remember that any member functions that you want the client (main function in this case) to use must be declared as public, so if you create any, put the getter functions after the public label.

As mentioned above, you have two options for reading and writing to the same file. The first (simpler) option is to open the file in loadData using an ifstream, read all the data into memory, and then close the file. At program termination, open the file using an ofstream, write all of the data in memory back to the file, and then close the file. Remember that when you open a file for output, if the file already exists, all of the data in the file is deleted. Your second option is to make the file object a member of the class, and then open the file for reading and appending. That way, you can open the file in loadData(), read all of the data, and then leave the file open. Then at program termination, just write the new data to the file. So if the user enters new exercise data, only that new data should be written to the file. If the user doesn’t enter new data, then nothing new gets written to the file. Remember to close all files either way. To open a file for reading and appending, create a file object and open it with the ios::in and ios::app options:

fstream file;

// ask the user for the fileName.

file.open(fileName, ios::in | ios::app); // one pipe (|) for bitwise ‘or’.

Explanation / Answer

Guideline #1. Each module with its .h and .cpp file should correspond to a clear piece of functionality. Conceptually, a module is a group of declarations and functions can be developed and maintained separately from other modules, and perhaps even reused in entirely different projects. Don’t force together into a module things that will be used or maintained separately, and don’t separate things that will always be used and maintained together. The Standard Library modules and are good examples of clearly distinct modules. Guideline #2. Always use “include guards” in a header file. The most compact form uses “ifndef”. Choose a guard symbol based on the header file name, since these symbols are easy to think up and the header file names are almost always unique in a project. Follow the convention of making the symbol all-caps. For example “Geometry_base.h” would start with: #ifndef GEOMETRY_BASE_H #define GEOMETRY_BASE_H and end with: #endif Guideline #3. All of the declarations needed to use a module must appear in its header file, and this file is always used to access the module. Thus #including the header file provides all the information necessary for code using the module to compile and link correctly; the header file contains the public interface for the module. Furthermore, if module A needs to use module X’s functionality, it should always #include “X.h”, and never contain hard-coded declarations for structs, classes, globals, or functions that appear in module X. Why? If module X is changed, but you forget to change the hard-coded declarations in module A, module A could easily fail with subtle run-time errors that won’t be detected by either the compiler or linker. This is a violation of the One-Definition Rule which C++ compilers and linkers can’t detect. Always referring to a module through its header file ensures that only a single set of declarations needs to be maintained, and helps enforce the One-Definition Rule. Guideline #4. No namespace using statements are allowed at the top level in a header file. See the Handout Using “using” for an explanation and guidelines. Usually you should be using fully qualified names in a header file, such as “std::ostream.” Guideline #5. The header file contains only declarations, templates, and inline function definitions, and is included by the .cpp file for the module. Put structure and class declarations, function prototypes, and global variable extern declarations, in the .h file; put the function definitions and global variable definitions and initializations in the .cpp file. The .cpp file for a module must include the .h file; the compiler will detect any discrepancies between the two, and thus help ensure consistency. Note that for templates, unless you are using explicit instantiations (rare), the compiler must have the full definitions available in order to instantiate the template, and so all templated function definitions must appear in the header file. Similarly, the compiler must have the full definitions available for ordinary (nonmember) functions that need to be inlined, so the function definitions will also appear (declared inline) in the header file (this is unusual, and normally won’t happen until late in project development during performance tuning). Guideline #6. Set up global variables for a module with an extern declaration in the header file, and a defining declaration in the .cpp file. For global variables that will be known throughout the program, place an extern declaration in the .h file, as in: extern int g_number_of_entities; The other modules #include only the .h file. The .cpp file for the module must include this same .h file, and near the beginning of the file, a defining declaration should appear - this declaration both defines and initializes the global variables, as in: int g_number_of_entities = 0; Of course, some other value besides zero could be used as the initial value, and static/global variables are initialized to zero by default; but initializing explicitly to zero is customary because it marks this declaration as the defining declaration, meaning that this is the unique point of definition. Note that different C compilers and linkers will allow other ways of setting up global variables, but this is the accepted C++ method for defining global variables and it also works for C to ensure that the global variables obey the One Definition Rule. Guideline #7. Keep a module’s internal declarations out of the header file. Sometimes a module uses strictly internal components that are not supposed to be accessed by other modules, or are not needed by other modules. The header file is supposed to contain the public interface for the module, and everything else is supposed to be hidden from outside view and access. Thus, if you need class or struct declarations, global variables, templates, or functions that are used only in the code in the .cpp file, put their definitions or declarations at convenient points in the .cpp file and do not mention them in the .h file. One common example is special-purpose function object class declarations for use with the Standard Library algorithms. Often these have absolutely no value outside the module, and so should not be simply tossed into the header file with other class declarations. It is better is to place their declarations in the .cpp file just ahead of the function that uses them. Furthermore, declare these internal global variables and functions static in the .cpp file to give them internal linkage (or put them into the unnamed namespace). Constants declared as const variables with initialization automatically get internal linkage (even if they appear in a header file), so declaring these as static is redundant and should not be done. This way, other modules do not (and can not) know about these declarations, globals, or functions that are internal to the module. The internal linkage will enable the linker to help you enforce your design decision. Guideline #8. Put declarations in the narrowest scope possible in the header file. Double-check Guideline #5 and make sure that a declaration belongs in the header file rather than the .cpp file for a module. If it does belong in the header file, place the declaration in the private section of a class if possible, followed by the protected section, followed by the public section of a class. Do not make it top-level in the header file unless it really needs to be that way.