TDT4102, Spring 2009
Exercise 9
Deadline: 27.03.2009
The objective of this exercise:
- Inheritance
- Virtual Functions
- Polymorphism
General requirements:
- use Visual Studio or another IDE of your choice
- use exact name and specification when given in the exercise
Recommended reading:
- Absolute C++ (Walter Savitch)
- It's Learning notes
Introduction
An implementation of the class Shape
can be found in the
file oving9.zip. Download the
file and import it into your project. In Visual C++, this can be
achieved by unzipping the file into the project directory. Use
the "add existing file" option to include the files into the project.
Shape is a simple generic class for any kind of geometric
two-dimensional shape
and it supports information about the color of a shape object and the
type
of line a shape object has. Both member variables are strings; the
color can be a value
such as "blue", "yellow", "green", and the line can be a value such as
"solid" or "dotted". Please note that the values used for
color and line are not very significant.
class Shape {
public:
Shape(string color, string line); //Constructor
void setColor(string color); //Used to change
the
color
void setLine(string line); //Used to change
the line
type
string getColor(); //Returns the color
string getLine(); //Returns the line type
string toString(); //Returns a string that
descibes
the shape in this format "Color = blue, Line = dotted"
private:
string color;
string line;
};
NB! The implementations in this
assignement are rather trivial but the main challenge is to understand
how inheritance, virtual funtions and plymorphism works.
Part 1: Simple inheritance, Rectangle (20 pt.)
a) Write a class named Rectangle that inherits the Shape class.
The Rectangle class is a subtype that extends the Shape class with
member variables for width and height and member functions that
calculate and
return area and circumference. The Rectangle class should include the
following member variables and functions.
- a private double width and double height variables
- Rectangle::Rectangle(string color, string line, double width,
double height), a constructor that initializes the object's
variables with the argument values.
- void Rectangle::setWidth(double width), sets the width of
the rectangle. If a negative number is given, the width should be set
to 0.0.
- void Rectangle::setHeight(double height), sets the height
of the rectangle. If a negative number is given, the height should be
set to 0.0.
- double Rectangle::getWidth(), returns the width of the
rectangle.
- double Rectangle::getHeight(), returns the height of the
rectangle.
- double Rectangle::getCircumference(), returns the
rectangle's circumference.
- double Rectangle::getArea(), returns the area of the
rectangle.
NB! The constructor for the Rectangle class should use the constructor
of the Shape superclass
to initialize the color and line member variables.
Part 2: Simple inheritance, Circle (20 pt.)
a) Write a class named Circle that inherits the Shape class. The
Circle class that extends Shape with a member variable for radius and
functions that calculates and
returns area and circumference. The Circle class should include the
following member variables and functions.
- a private double radius variable
- Circle::Circle(string color, string line, double radius),
a constructor that initializes the object's variables with the argument
values.
- void Circle::setRadius(double width), sets the radius
of a circle. If a negative number is given, the width should be set to
0.0.
- double Circle::getRadius(), returns the radius.
- double Circle::getCircumference(), returns
circumference of the circle.
- double Circle::getArea(), returns the area of the
rectangle.
NB! The constructor for the Circle class should use the constructor of
the Shape superclass
to initialize the color and line member variables.
Part 3: Three-level inheritance, Square (20 pt.)
a) Write a Square class, which inherits the Rectangle class.
Extend the class with the following member function:
- Square::Square(string color, string line, double sideLength),
a constructor that initializes the object's
variables with the argument values.
NB! The constructor of the Square class should use the constructor of
the Rectangle
superclass to initialize the member variables. A square has equal sides
and the sideLenght argument can be used as both the width and the
hight.
Part 4: Redefining the toString() funtion (20 pt.)
a) Redefine the toString() function for all the subclasses that
inherits Shape.
- The toString() function in the Rectangle class should return a
string that includes color, line type, hight, width, area and
circumference, eg: "Color = blue, Line = dotted, Height = 8, Width = 5,
Area = 40, Circumference = 26"
- The toString() function in the Circle class should return a
string
that includes color, line type, radius, area and circumference, eg:
"Color = blue, Line = dotted, Radius = 5, Area = 78.5, Circumference =
31.4"
- Do you need to redefine the toString() function for the Square
class?
NOTE! You should implement the toString() functions in such a way that
the redefined functions make use of the toString() function of the
parent
class!
The toString() function in the Rectangle class should return a string
value that is constructed from the value that is returned by the
toString() function of the Shape class and additional information that
is specific for the Rectangle class.
HINT! The class stringstream can be
used to concatenate values of different types into a string.
This class is defined in <sstream> and you typically use the
insertion operator << to add data to a stringstream.
The function str() returns the content of a stringstream as a string;
#include <sstream>
stringstream ss;
ss << "tekst";
ss << 5;
string s = ss.str();
Part 5: Virtual functions (20 pt.)
a) The toString() function in the Shape class can be defined as
virtual.
Cut and paste the following code into your program. Compile and run the
program without declaring
the toString() as virtual first. Compile and run the program with the toString()
function declared as virtual afterwards.
What is the difference? Make sure you understand why the output is
different.
Shape s1("red", "dotted");
cout << "s1: " << s1.toString() <<
endl;
Rectangle r1("blue", "solid", 6, 6);
cout << "r1: " <<r1.toString() <<
endl;
Shape s2 = r1;
cout << "s2: " <<s2.toString() <<
endl;
Shape *ps1 = new Shape("green", "dotted");
cout << "ps1: " <<ps1->toString()
<< endl;
Rectangle *pr1 = new Rectangle("yellow", "solid", 5,
5);
cout << "pr1: " << pr1->toString()
<< endl;
Shape *ps2 = pr1;
cout << "ps2: " << ps2->toString()
<< endl;
b) The Shape class can be made into an abstract class. Instances
you make in your program should be of a specific subtype as there are
no shape objects
without any specific form. Create the following function:
virtual string getName() = 0;
as
a pure virtual function for the Shape class.
- For the derived classes, this function should return a string
with
the name of the shape class (i.e.
"Rectangle", "Circle", "Square")
Change the implementation of toString() in the Shape class and include
the string from getName() in the final string that is returned.
The
toString() function in the Shape class should now be responsible for
returning a string such as this: "Shape = Rectangle,
Color = blue, Line = solid"
c) Create a vector that can contain pointers to Shape-objects
e.g.
vector<Shape *> shapes;
Create some rectangles, squares, circles etc. and add them to the
vector (remember that the vector has stores pointers to shapes). Write
a
simple loop that
calls the toString() function on all the shapes in the vector and
prints out the returned string.
Use the debugger to inspect the sequence of function calls that
actually happen for each call to toString().
OPTIONAL: Include the area and circumference in the string that is
returned from the
toString() function for all shapes. Use pure virtual getArea()
and getCircumference functions for this purpose.