Write a complete C++ class to represent a circle in two-dimensional space. Your class should consist of a header (circle.h) and an implementation (circle.cpp) file as described below. The circle class should have the folllowing (private) attributes and (public) methods:
C++ classes are made up of a header file and an implementation file. Both files should have the same name except that the header file has a .h extension while the implementation has a .cpp file. The header file contains the class interface, and the .cpp file contains the implementation.
The header file consists of the class name, and
the name (and type) of the member variables and the header for each of
the methods. The .cpp file consists of the definition for each of
the class methods.
Class member variables and methods should be specified as being either private or public. Private variables or methods can only be accessed from within the class, whereas public variables and methods can be accessed from outside the class. There are a couple of good general design principles to follow when deciding whether or not to make a method or variable public:
Every
class requires a constructor that creates new objects of that
class. A class will often have multiple constructors that build
new objects in slightly different ways. A constructor is a method
that has exactly the same name as the class and has no return type, it
is responsible for setting the initial values of the member variables
of an object.
C++ classes require destructors. A destructor is responsible for de-allocating any dynamic memory that an object uses. If you don't write a destructor for a class a default one is created for you. It is OK to rely on the default destructor as your class does not use any dynamic memory, if it does, then you must write a destructor.
We'll discuss constructors and destructors at greater length in class.
It's easier to show the syntax with a simple example. Here is a class that represents a rectangle, it will have the following member variables and methods:
Here is the header file, C++ keywords are in bold.
// Rectangle.h file // Definition of a rectangle class class Rectangle { public: //everything that follows is public //Default constructor Rectangle(); //the default constructor has no parameters // Constructor to create a new rectangle with the given values Rectangle(int w, int h); // Setters that change the values of the attributes void setWidth(int w); void setHeight(int h); // Getters that return information about the rectangle, note the const at the end of the method // this guarantees that the method cannot alter the member variables int getWidth() const; int getHeight() const; int getArea() const; // Display method that prints the rectangle's height and width void displayRectangle() const; private: //everything that follows is private and cannot be "seen" from outside the class // Some classes have private methods, this one doesn't! int width; int height; }; //Rectangle - note the ";" - don't forget it! // End of the header fileAnd here is the implementation file:
// Rectangle.cpp file #include <iostream> // As we need to print data #include "Rectangle.h" // The header file for the class - we need this! using namespace std; /* * Now follows each of the method implementations. The <class>:: that precedes each method identifies * that the function belongs to the class. If it is omitted the compiler will attempt to create a separate * function, which may or may not be successful. */ //Default constructor Rectangle::Rectangle(){ width = 1; height = 1; }//Rectangle() // Constructor to create a new rectangle with the given values Rectangle::Rectangle(int w, int h) { if (w > 0) width = w; // don't need {}s if there is only one line in the body else width = 1; if (h > 0){ height = h; // but you can use them if you want } else{ height = 1; } }//Rectangle(int, int) // Setters that change the values of the attributes void Rectangle::setWidth(int w) { if (w > 0) width = w; else width = 1; }//setWidth void Rectangle::setHeight(int h) { if (h > 0) height = h; else height = 1; }//setHeight // Getters that return information about the rectangle int Rectangle::getWidth() const { return width; }//getWidth int Rectangle::getHeight() const{ return height; }//getHeight int Rectangle::getArea() const { return width * height; }//getArea // Display method that prints the rectangle's height and width void Rectangle::displayRectangle() const { cout << endl << "width = " << getWidth(); cout << ", height = " << getHeight() << endl; }//displayRectangle // End of the implementation file
There is (a lot) more to C++ classes than described above but this is enough to get you started.
If you actually want to use and test your circle class, you will need a main function which you will compile to use with it. This program is often known as a driver program.
As an example, for testing your circle class I have provided 2 driver programs. Download this zip file and save and unzip it in your lab2 directory. This zipfile contains testcirc1.cpp and testcirc2.cpp, the two driver programs. Each one creates some circles and tests methods. testcirc1.cpp tests the getters and setters. testcirc2.cpp tests intersect.
Compile these using the following g++ commands:
g++ -o testcirc1 circle.cpp testcirc1.cpp g++ -o testcirc2 circle.cpp testcirc2.cpp
There is also a test script (again test.py), which you can run:
uname@hostname: ~$ ./test.pyIf you have correctly built executables testcirc1 and testcirc2 that solve this lab you will see:
Running test 1... passed Running test 2... passed Passed 2 of 2 tests.
If you have passed at least 1 of 2 tests, please ask a TA to take a look at this output, and receive your 1 mark for this lab.
You should also complete the rest of this lab below, but this can be done as homework if you do not have time to complete it in your lab time slot.
If you wanted to compile your circle class on its own, you could do so. But wait, there's no main function?! You can compile your circle class using the -c option in g++:
uname@hostname: ~$ g++ -c circle.cpp uname@hostname: ~$ ls circle.cpp circle.h circle.oBut wait, what does it mean to compile a class with no main function? Why would you want to do such a thing? What is that circle.o file that was just created?
circle.o is called an object file. One way to think about object files is that they contain machine instructions for the functions in a class -- they specify how to run the functions. However, nobody is using these functions yet, since there is no main function. So it's as if you have compiled code which is just a set of functions, without any specification of in which order these functions should be called, and on what data.
Why would you want to do this? (1) To make sure some part of your code compiles. (2) To reuse the compilation -- don't need to recompile circle.cpp if you change testcirc.cpp. This may seem trivial in a small example like this one, but makes a big difference in more complex software with many classes.
For example, you can compile testcirc.cpp using the circle.o object file:
uname@hostname: ~$ g++ -o test testcirc1.cpp circle.oTechnically, this process involves linking, in which the compiled machine instructions in circle.o are linked into the new executable test, rather than being recompiled.
Perhaps you're thinking this sounds like a pain. First I ran a g++ command on circle.cpp, then I had to type in another long g++ command to compile testcirc.cpp. It would be great if there were some way to avoid this. Of course, there is.
uname@hostname: ~$ make cleanThis command will remove any .o files as well as the executables testcirc1 and testcirc2 you may have already created. Now try:
uname@hostname: ~$ make testcirc1If you have code that properly compiles, you should see:
g++ -c testcirc1.cpp g++ -c circle.cpp g++ testcirc1.o circle.o -o testcirc1Magic! The recipe in Makefile specifies how to build testcirc1. The executable testcirc1 should now exist.
Now try making a small change to testcirc1.cpp (anything at all -- just try adding an empty line in the file). If you again run make testcirc1, you should see:
uname@hostname: ~$ make testcirc1 g++ -c testcirc1.cpp g++ testcirc1.o circle.o -o testcirc1Note that circle.cpp was not recompiled.
Makefiles also have a default, called "all." If you run "make all" or just "make", it will build everything specified for all. In this case, this is testcirc1 and testcirc2.
uname@hostname: ~$ make clean rm -f testcirc1 testcirc2 *.o uname@hostname: ~$ make g++ -c testcirc1.cpp g++ -c circle.cpp g++ testcirc1.o circle.o -o testcirc1 g++ -c testcirc2.cpp g++ testcirc2.o circle.o -o testcirc2You will now have both of your driver programs, and can run test.py on them.
Makefiles allow you to easily recompile your code after making changes, and while debugging. So from now on, you don't need to keep typing in g++ commands, you can instead just run "make".
Of course, there is a lot more to makefiles than what I've written here. In this course, I will never ask you to create/modify makefiles. I will always provide them to you if they are needed. In fact, integrated development environments (IDEs) such as MS Visual Studio or Eclipse are essentially doing this in their "projects." But as a computer scientist, it's important for you to know what's happening "under the hood."