Shape *myShape = new Point(5, 6, 'O');
Due Wednesday, September 27th at 11:59pm.
In this lab, you will design a hierarchy of classes. You will become familiar with the following in C++:
Classes and Objects
Inheritance
Polymorphism
Memory management
As with the previous labs, you should clone your repository. The URL is:
git@github.swarthmore.edu:CS35-F23/lab03-<your-username>
We will describe this lab in three parts, the last of which has been provided for you. You are strongly encouraged to read this entire lab write-up when you begin this assignment! Later in the write-up, we give suggestions on how to get started.
The first part of this lab involves defining classes that represent different kinds of shapes. Each of our shapes may look different, but they all have something in common: we can draw them onto a grid.
By default the grid will be 20 wide and 10 high. You may assume that the user always provides valid coordinates that lie within the grid’s boundaries.
A Shape
class is provided for you in shape.h
. This is a purely virtual class and therefore we do not need a shape.cpp
file.
You need to do the following:
Define a Point
class in point.h
and point.cpp
Define a HorizontalLine
class in horizontalLine.h
and horizontalLine.cpp
Define a VerticalLine
class in verticalLine.h
and verticalLine.cpp
Define a Rectangle
class in rectangle.h
and rectangle.cpp
Class declarations go in the header files (.h
) while class implementations go in the source files (.cpp
).
The following sections describe specifications for the Rectangle
, HorizontalLine
, VerticalLine
, and Point
classes. You must abide by these specifications.
Your Point
class is a subclass of Shape
. It must contain a constructor that takes as input (order matters):
two int
parameters for x position and y position
one char
parameter for display symbol
It must contain the following private member variables:
int
variables x
and y
char
variable symbol
Because the Point
class is a subclass of Shape
, it must contain a public method called draw
with one input argument of type Grid *
and return type void
. The draw
method places the display symbol onto the grid at the appropriate location.
For example, consider a Point
with x position 5
, y position 6
, and display symbol 'O'
. We create this point using the following code:
Shape *myShape = new Point(5, 6, 'O');
When drawn on the grid, the point will appear as follows. Recall that the upper left corner of the grid has coordinate (0,0), just as in the PicFilter lab.
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * O * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
Your HorizontalLine
and VerticalLine
classes are also subclasses of Shape
. They must contain constructors that take as input (order matters):
three int
parameters for x position, y position, and length
one char
parameter for display symbol
They must contain the following private member variables:
int
variables x
, y
, and length
char
variable symbol
As with Point
, the HorizontalLine
and VerticalLine
classes must implement the draw
method. A HorizontalLine
object starts at the given x and y coordinates and extends to the right occupying cells on the grid. The number of cells that the HorizontalLine occupies is equal to its length
. A VerticalLine
object is the same but extends down occupying cells.
For example, let us consider a grid with two shapes:
a HorizontalLine
with x position 2, y position 5, length 3, and display symbol '$'; and
a VerticalLine
with x position 5, y position 0, length 4, and display symbol '@'
We create these shapes using the following code:
Shape *myShape1 = new HorizontalLine(2, 5, 3, '$'); Shape *myShape2 = new VerticalLine(5, 0, 4, '@');
The lines would appear on the grid as follows:
* * * * * @ * * * * * * * * * * * * * *
* * * * * @ * * * * * * * * * * * * * *
* * * * * @ * * * * * * * * * * * * * *
* * * * * @ * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * $ $ $ * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
Your Rectangle
class is also a subclass of Shape
. It must contain a constructor that takes as input (order matters):
four int
parameters for x position, y position, width, and height
one char
parameter for display symbol
It must contain the following private member variables:
int
variables x
, y
, width
, and height
char
variable symbol
As a Rectangle
is also a subclass of Shape
, it too must implement the draw
method correctly. We will draw solid rectangles that are filled-in with the rectangle’s symbol.
For example, consider a Rectangle
with x position 3, y position 0, width 5, height 3, and display symbol 'X'.
We create this rectangle using the following code:
Shape *myShape = new Rectangle(3, 0, 5, 3, 'X');
The rectangle would appear on the grid as follows:
* * * X X X X X * * * * * * * * * * * *
* * * X X X X X * * * * * * * * * * * *
* * * X X X X X * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
With an idea of what shapes are, we can define the idea of a "picture": a set of shapes drawn in specified order. The shapes in a picture have a specified order because we may draw shapes on top of other shapes.
The design for the Picture
class is provided for you in picture.h
. It’s your job to fill in the implementation for the Picture
class in picture.cpp
.
The Picture class contains the following private member variables:
int numberOfShapes
Shape **shapes
The numberOfShapes
variable keeps track of how many shapes have been added to the picture. The shapes
variable keeps track of those shapes.
The shapes
variable has the type Shape **
, which is new to our labs. Just as an int *
variable can be used to point to an array of int
values, a Shape **
variable can be used to point to an array of Shape *
values. That is, the Shape **
type of the shapes
variable can be read as "a pointer to an array of pointers to Shape
objects". Because the array does not require a specific type of Shape
, the pointers may point to a mixed collection of shapes: Point
, HorizontalLine
, VerticalLine
, and Rectangle
objects.
To complete the Picture
class, you will need to complete the following tasks.
Implement a Picture
constructor. The constructor must prepare the Picture
to be used, so you will need to initialize the numberOfShapes
and shapes
variables. Your Picture
starts with no shapes in it but must be ready to add shapes in the future. You may assume that users will never add more than fifty shapes to a picture, so you may initialize the shapes
variable to an array of size 50
.
Implement a Picture
destructor (named ~Picture
). The destructor must clean up everything in the picture. Since shapes added to a picture will be owned by that picture, the ~Picture
destructor is responsible for deleting them.
Implement an addShape
method. This method takes a Shape *
that points to a shape and adds it to the picture. (You’ll need to keep it in your array so you can draw it later.)
Implement a toString
method. This method should create a grid, draw all of the Shape
s onto it, and then convert the grid to a string that you return. (Remember: if you dynamically allocate any objects during this process, you’ll need to clean them up!)
The third part of this assignment is a program called artwork
that allows the user to draw a picture using the classes you have defined. Don’t worry: the artwork
program has been written for you! Once you have defined a Shape
class and a Picture
class, you can run make
followed by ./artwork
and experiment with the classes you have built.
When you first clone your Git repository for this assignment, it will contain the following starter files. Those that are highlighted require modification.
Makefile
- instructions for compiling your program.
shape.h
- design for virtual Shape class
rectangle.h
, rectangle.cpp
- design & implementation for Rectangle class
horizontalLine.h
, horizontalLine.cpp
- design & implementation for HorizontalLine class
verticalLine.h
, verticalLine.cpp
- design & implementation for VerticalLine class
point.h
, point.cpp
- design & implementation for Point class
picture.h
, picture.cpp
- design & implementation for Picture class
grid.h
, grid.cpp
- design & implementation for Grid class
artwork.cpp
- main methods for running application that creates shapes
shapeFactory.h
, shapeFactory.cpp
- behind the scenes code for creating shapes
tests.cpp
, manualTests.cpp
- programs that can test your code
Note: A generally good strategy for tackling this lab is to start by implementing Point
(the Shape
with the shortest draw
method), then to implement Picture
(so you can start testing with the artwork
program), and then to implement the remaining shapes.
There are at least three ways to test your code in this lab:
You can write code in the main
function of the file manualTests.cpp
. Then, you can run make manualTests
and ./manualTests
. This gives you a playground in which you can experiment with the classes and objects you are writing. You can even run gdb
or valgrind
on manualTests
to track down bugs in your classes. See below for an example of how to draw multiple shapes onto a picture and print them.
Once you have implemented Picture
and at least one Shape
, you can run make
and ./artwork
. This will allow you to draw shapes onto a picture. You can use this even if you haven’t written all of the Shape
subclasses; it just won’t work for the shapes you haven’t finished yet.
You can run make tests
followed by ./tests
to run some automated tests. You can run these tests before you’re finished with the lab; even though many tests will fail, the names of those tests should indicate whether the failure is because you have not yet finished the lab or because something is wrong with what you have written. For instance, if you have finished Point
but not Rectangle
, you can ignore failing tests with "rectangle" in their names, but failing tests with "point" in their names might indicate that you need to fix your code before moving on.
The following creates three rectangles:
Shape *myShape1 = new Rectangle(0, 0, 7, 7, '$'); Shape *myShape2 = new Rectangle(1, 1, 6, 4, '-'); Shape *myShape3 = new Rectangle(3, 2, 4, 3, 'z');
The following adds the three shapes to a picture:
Picture myPicture; myPicture.addShape(myShape1); myPicture.addShape(myShape2); myPicture.addShape(myShape3);
The following will draw the shapes one after another onto a Grid and print the result to the command line:
myPicture.print();
The result looks as follows:
$ $ $ $ $ $ $ * * * * * * * * * * * * *
$ - - - - - - * * * * * * * * * * * * *
$ - - z z z z * * * * * * * * * * * * *
$ - - z z z z * * * * * * * * * * * * *
$ - - z z z z * * * * * * * * * * * * *
$ $ $ $ $ $ $ * * * * * * * * * * * * *
$ $ $ $ $ $ $ * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
You will also be required to observe good coding practices:
Your C++ code must have proper and consistent indentations.
You must have proper and consistent usage of spacing and braces for if
, else
, for
, and while
conditions as well as class definitions and code blocks.
To summarize the requirements of this lab:
You must declare and define four subclasses of Shape
: Point
, HorizontalLine
, VerticalLine
, and Rectangle
.
You must define a Picture
class that contains many Shape
s and draws them all onto a Grid
.
You should be able to run make tests
and ./tests
on the lab machines without any errors.
Your program should not have any memory leaks or errors.