In: Computer Science
Data Structures
Use good style. Make sure that you properly separate code into .h/.cpp files. Make sure that you add preprocessor guards to the .h files to allow multiple #includes.
Overview
You will be writing the classes Grade and GradeCollection. You will be writing testing code for the Grade and GradeCollection classes.
Part 1 – Create a Grade Class
Write a class named Grade to store grade information.
Grade Class Specifications
Part 2 – Create a GradeCollection Class
Write a class that will store a collection of Grade. This class will be used to keep track of data for multiple grades. You MUST implement ALL of the specifications below.
GradeCollection Class Specifications
If lowerBound is 70 and upperBound is 80 then the returned value should be 3. Any values that fall on the boundaries should be included in the count.
Part 3 – Main Function
In main you should create instances of the Grade and GradeCollection classes and demonstrate that ALL functions work properly on both classes. You can write unit testing code if you want but you are not required to. Make sure you call ALL functions.
Part 4 – Comments
In addition to the normal comments, EVERY function that gets updated because of the required changes should have an update comment added to the function commenting header. The update comment should have your name, the date of the change, and a short description of the change. For example:
//****************************************************
// Function: SetName
//
// Purpose: Sets the name of the grade.
//
// Update Information
// ------------------
//
// Name:
// Date: 9/20/2016
// Description: mName member variable was changed to a
// pointer. Updated code so that it works // with a pointer.
//
//****************************************************
Part 5 – Updated Grade and GradeCollection Classes
Grade Class Updates
The Grade class should implement all the specifications from the first assignment plus the updates and features listed below.
std::string GetName(); void SetName(std::string name);
These signatures should remain exactly the same. The same goes for any other functions that use these member variables. Only the internal implementation of the functions will change to accommodate the use of pointers.
GradeCollection Class Updates
The GradeCollection class should implement all the specifications from the first assignment plus the updates and features listed below.
This function should create a new array that has the passed in size. You MUST retain any values that were previously in the array. The new array size can be larger or smaller. If the new array size is SMALLER just retain as many elements from the previous array that can fit.
Hint: C++ arrays have a fixed size. You may need to delete and then reallocate memory. Be careful for memory leaks.
GradeCollection *Clone();
This function should allocate a new dynamic instance of GradeCollection that is a deep copy of the current instance. This method should return a pointer to the new instance.
Hint: Any function that calls Clone is responsible for releasing the returned memory address.
Part 6 – Main Function
In main you should create instances of the updated Grade and GradeCollection classes and demonstrate that ALL functions work properly. You can write unit testing code if you want but you are not required to. Make sure you call ALL functions.
You program should not have memory leaks.
C++ Separate Header and Implementation Files C++ classes (and often function prototypes) are normally split up into two files. The header file has the extension of .h and contains class definitions and functions. The implementation of the class goes into the .cpp file. By doing this, if your class implementation doesn’t change then it won’t need to be recompiled. Most IDE’s will do this for you – they will only recompile the classes that have changed. This is possible when they are split up this way, but it isn’t possible if everything is in one file (or if the implementation is all part of the header file). Simple example: File: Num.h class Num { private: int num; public: Num(int n); int getNum(); }; File: Num.cpp #include "Num.h" Num::Num() : num(0) { } Num::Num(int n): num(n) {} int Num::getNum() { return num; } File: main.cpp #include #include "Num.h" using namespace std; int main() { Num n(35); cout #include "Num.h" #include "Foo.h" using namespace std; int main() { Num n(35); cout #endif This says if “NUM_H” is not defined, then define it. So subsequent attempts to read this class result in skipping the definition since NUM_H is already defined. If we add this to our Num.h class then it will now compile and run. You can use any name but the recommendation is to use a name related to the class. The #pragma once directive The same functionality as #ifndef can be accomplished by adding #pragma once to the top of your file. This is the default used with Visual Studio. Separate Compilation With what we’ve done so far we split the header from the implementation. We’re actually still compiling both all the time though when we run the g++ command. To really get separate compilation we need to: 1. Compile each .cpp file into an object file, which contains machine code for that file 2. Link each object file into an executable To compile into an object file, you can use the command flag of –c: g++ -c main.cpp Num.cpp This produces a file main.o and Num.o. We can then link them. We can use g++ again to do this: g++ main.o Num.o We now have our familiar a.out program which we can run. An efficiency step is possible here because if Num never changes but main does change, then we can compile just main via: g++ -c main.cpp Then we can link them again but we didn’t have the overhead of compiling Num.cpp: g++ main.o Num.o With our little programs this saves virtually no time, but for really large programs this can be a significant savings in compile time. All of this stuff is done for you automatically by an IDE (which is one reason why they are great). The make utility It can also be a pain to compile lots of files when you have a big project. Once again, this functionality is provided for you as part of an IDE. But you have to do it yourself if you are compiling from the command line. Well, there is a solution in the make utility. If you run the command “make” then this program will look for a file named “Makefile” that contains information about program dependencies and what needs to be compiled. Here is an example for our particular setup: CFLAGS = -O CC = g++ NumTest: main.o Num.o $(CC) $(CFLAGS) -o NumTest main.o Num.o main.o: main.cpp $(CC) $(CFLAGS) -c main.cpp Num.o: Num.cpp $(CC) $(CFLAGS) -c Num.cpp clean: rm -f core *.o This says that NumTest depends on main.o and Num.o and the second line says how to compile it. In turn, main.o is created by compiling main.cpp with the –c flag. We can compile by typing: make or make NumTest We can use the target of “clean” to delete object files and core dumps. Only files that have changed are recompiled. Here is a similar version using some more advanced features of Makefiles: CFLAGS = -O CC = g++ SRC = main.cpp Num.cpp OBJ = $(SRC:.cpp = .o) NumTest: $(OBJ) $(CC) $(CFLAGS) -o NumTest $(OBJ) clean: rm -f core *.o