Description
1 Objectives
• Practice fundamental object-oriented programming (OOP) concepts
• Implement an inheritance hierarchy of classes
• Use virtual functions, overriding, and polymorphism in C++
• Use two-dimensional arrays using array and vector, the two simplest container class
templates in the C++ Standard Template Library (STL)
• Use C++ smart pointers
2 Modeling Geometric Shapes
Using simple geometric shapes, this assignment will give you practice with fundamental concepts
of OOP, namely, the concepts of abstraction, encapsulation, information hiding, inheritance, and
polymorphism.
The geometric shapes considered are simple two-dimensional shapes that can be reasonably
depicted textually on the computer screen; specifically, rhombus , rectangle, and two kinds of
triangles. As depicted in the UML class diagram on page 1, the classes that model our shapes
of interest in this assignment form a single inheritance hierarchy, with the most generalized class
Shape at the top.
Shape
Triangle Rectangle Rhombus
AcuteTriangle RightTriangle
Figure 1: A UML class diagram showing Shape inheritance hierarchy.
So let’s begin with class Shape; that is, let’s begin by specifying the common characteristics of
our simple geometric shapes.
1
2.1 Common Attributes of Shape Objects: State (Data)
A integer identity number, distinct from that of the rest of the Shape objects.
An optional user supplied name for the shape, such as “Swimming Pool.”
An optional user supplied description for the shape, such as “Montreal’s Olympic Stadium”.
2.2 Common Operations of Shape Objects: Interface
1. A constructor that optionally accepts initial values for the shape’s name and description.
2. Three accessor (getter) methods, one for each attribute;
3. Two mutator (setter) methods to set the object’s name and description;
4. A method that generates and returns a string representation for the shape object;
5. A method to compute the object’s geometric area;
6. A method to compute the object’s geometric perimeter;
7. A method to compute the object’s screen area; that is, the number of characters that form
the textual image of the shape;
8. A method to compute the object’s screen perimeter; that is, the number of characters on
the borders of the textual image of the shape;
9. A method that draws a textual image for the shape object on a two dimensional grid, and
then returns that grid.
10. Two methods returning, respectively, the height and the width of the object’s bounding
box; that is, the smallest rectangular box enclosing the textual image of the shape.
3 Abstract Shapes
The UML class1 diagram shown on page 1 represents two abstract classes: Shape and Triangle.
Encapsulating the attributes and operations common to its objects, class Shape is abstract
because the shapes it models are so general that it would not know how to implement most of
the operations it is required to provide; for example, operation 9, to name just one example. As
an abstract class, Shape not only serves as a common interface to all classes in the inheritance
hierarchy, but also makes polymorphism possible through variables of types Shape* and Shape&.
Based on Shape, class Triangle models triangular shapes with their bases oriented horizontally.
The height of a triangle is length of the line perpendicular to the base from the intersection of
the other two sides.
Obviously, class Triangle must also be abstract as it too lacks information to implement some
of the operations it inherits from Shape, including operation 9.
1Recall that in UML the name of an abstract class is written in an italic font.
2
4 Concrete Shapes
Rectangle, Rhombus, RightTriangle and AcuteTriangle represent concrete shape classes,
picked specifically because they each can be textually rendered into visually identifiable patterns.
The specific features of these concrete shapes are listed in the following table.
Specialized Features of Concrete Shapes
Features Concrete Shapes
Shape name Rectangle Rhombus Right Triangle Acute Triangle
Construction values h, w d, if d is even
set d←d + 1 b
b, if b is even set
b←b + 1
Computed values h = b h = (b + 1)/2
Height of bounding box h d h h
Width of bounding box w d b b
Geometric area hw d
2/2 hb/2 hb/2
Screen area hw 2n(n+1)+1,
n = bd/2c
h(h + 1)/2 h
2
Geometric perimeter 2(h + w) (2√
2)d (2 + √
2)h b + 2√
0.25b
2 + h
2
Screen perimeter 2(h + w) − 4 2(d − 1) 3(h − 1) 4(h − 1)
Sample visual
pattern
*********
*********
*********
*********
*********
*
***
*****
***
*
*
**
***
****
*****
*
***
*****
*******
*********
Sample pattern
dimensions
w = 9, h = 5 d = 5 b = 5, h = b b = 9, h =
b+1
2
3
4.1 Shape Notes
The unit length is a character; thus, the lengths of the vertical and horizontal attributes of
a shape are measured in characters.
The height and width of a shape’s bounding box are NOT stored anywhere; they are
computed on demand.
At construction, a Rectangle shape requires the values of both its height and width,
whereas the other three concrete shapes each require a single value for the length of their
respective horizontal attribute.
In addition, at construction, all shape objects accept optional user specified name and
description. The default values are given in Table 1.
Shape Default shape name Default shape description
Rhombus Diamond A parallelogram with equal sides
Rectangle Four-sided Four right angles
Triangle
Acute Acute triangle All acute angels
Right Right triangle One right and two acute angles
Table 1: Default shape names and descriptions
5 Task 1 of 2
Implement the Shape inheritance class hierarchy described above. It is completely up to you to
decide which operations should be virtual, pure virtual, or non-virtual, provided that it satisfies a
few simple requirements. Feel free to add your own private methods, if you think they facilitate
your implementaion of the class interface.
The amount of coding required for this task is not a lot as your shape classes will be small. Be
sure that common behavior (shared methods) and common attributes (shared data) are pushed
toward the top of your class hierarchy.
4
5.1 Some Examples
Sourse code
1 Rectangle rect { 5, 7 };
2 cout << rect . toString () << endl ;
3 // or equivalently
4 // cout << rect << endl ;
Output
1 Shape Information
2 -----------------
3 id: 1
4 Shape name : Four - sided
5 Description : Four right angles
6 B. box width : 5
7 B. box height : 7
8 Scr area : 35
9 Geo area : 35.00
10 Scr perimeter : 20
11 Geo perimeter : 24.00
12 Static type : PK5 Shape
13 Dynamic type : 9 Rectangle
The call rect.toString() on line 2 of source code generates the entire output shown. However,
note that line 4 would produce the same output, as the output operator overload itself internally
calls toString().
Line 3 of the output shows that rect’s ID number is 1. The ID number of the next shape will be
2, the one after 3, and so on. These unique ID numbers are generated and assigned when shape
objects are first constructed.
Line 4-5 of the output show object rect’s name and description, and lines 6-7 show the width
and height of rect’s bounding box, respectively.
Now let’s see how the static and dynamic types in lines 12-13 of the output are produced.
Recall that a C++ pointer (or reference) to a class with a virtual member function has two types:
static type and dynamic type; the static type is its type as defined in the source code, and thus
cannot change, and the dynamic type is the type of the object it points at (or references) at
runtime, and thus may change during the runtime period.
To get the name of the static type of a pointer p at runtime you use typeid(p).name(), and to
get its dynamic type you use typeid(*p).name(). That’s exactly what toString() does at line
2, using this instead of p. You need to include the header for this.
As you can see on lines 12-13, rect’s static type name is PK5Shape and it’s dynamic type
name is 9Rectangle. The actual names returned by these calls are implementation defined. For
example, the output above was generated under g++ (GCC) 7.4.0, where PK in PK5Shape
means “pointer to konst const”, and 5 in 5Shape means that the name “Shape” that follows
it is 5 character long.
Microsoft VC++ produces a more readable output as shown below.
5
1 Rectangle rect { 5, 7 };
2 cout << rect . toString () << endl ;
3 // or equivalently
4 // cout << rect << endl ;
Shape Information
-----------------
id: 1
Shape name : Four - sided
Description : Four right angles
B. box width : 5
B. box height : 7
Scr area : 35
Geo area : 35.00
Scr perimeter : 20
Geo perimeter : 24.00
Static type : class Shape const *
Dynamic type : class Rectangle
Note that in the source code above the object rect is NOT a pointer (or reference) and hence
NOT polymorphic; however, the call rect.toString() on line 2 represents rect inside toString()
by the this pointer, where object *this is polymorphic.
Here is an example of a Rhombus object:
5 Rhombus
6 ace{ 16, "Ace", "Ace of diamond " };
7 // cout << ace . toString () << endl ;
8 // or , equivalently :
9 cout << ace << endl ;
Shape Information
-----------------
id: 2
Shape name : Ace
Description : Ace of diamond
B. box width : 17
B. box height : 17
Scr area : 145
Geo area : 144.50
Scr perimeter : 32
Geo perimeter : 48.08
Static type : class Shape const *
Dynamic type : class Rhombus
Notice that in line 6, the supplied height, 16, is invalid because it is even; to correct it, Rhombus’s
constructor uses the next odd integer, 17, as the diagonal of object ace.
Again, lines 7 and 9 would produce the same output; the difference is that the call to toString()
is implicit in line 9.
Here are examples of AcuteTriangle and RightTriangle shape objects.
6
10 AcuteTriangle at{ 17 };;
11 cout << at << endl ;
12
13 /* equivalently :
14
15 Shape * atptr = &at;
16 cout << * atptr << endl ;
17
18 Shape & atptr = at;
19 cout << atptr << endl ;
20 */
Shape Information
-----------------
id: 3
Shape name : Acute triangle
Description : All acute angels
B. box width : 17
B. box height : 9
Scr area : 81
Geo area : 76.50
Scr perimeter : 32
Geo perimeter : 41.76
Static type : class Shape const *
Dynamic type : class AcuteTriangle
21 RightTriangle
22 rt{ 10, " Carpenter ’s square " };
23 cout << rt << endl ;
Shape Information
-----------------
id: 4
Shape name : Carpenter ’s square
Description : One right and two acute angles
B. box width : 10
B. box height : 10
Scr area : 55
Geo area : 50.00
Scr perimeter : 27
Geo perimeter : 34.14
Static type : class Shape const *
Dynamic type : class RightTriangle
5.2 Polymorphic Magic
It is important to note that none of the objects rect, ace , at, and rt is polymorphic because none
of them is a pointer or a reference. Polymorphic magic happens through the second argument
in the calls to the output operator<< at lines 2, 8, 11, and 23. For example, consider the call
cout<