CS-321 Computer Graphics
Lab 2: Derived and Container Classes
Course Outcomes Addressed
- Be able to apply concepts of object-oriented programming, inheritance,
polymorphism using C++
- Apply data structures to the management of computer graphics
entities.
- Use reference materials to gain knowledge of an unfamiliar
software library
- Explain the need for extensive internal software documentation, and
be able to provide it.
Overview
The purpose of this lab is to review and renew basic skills in the programming
of C++ derived and container classes. Mastery of these is important in order to
complete all future labs in this course.Preparation
Review your course materials from CS1030 that pertain to the C++ mechanisms for
base classes, derived classes, inheritance, and the use of the C++ Standard
Template Library (STL). You may
find this glossary useful.
Background
The field of computer graphics lends itself quite well to the use of base
classes and derived classes. The ability of derived classes to inherit
properties from a base class so that they do not have to be rewritten is quite
powerful. In addition the ability to override and customize methods from the
base class and to add new methods allows a derived class to customize itself for
its own needs.
The files shape.h and
shape.cpp declare and
implement an abstract base class called shape that might be appropriate for
generic graphical shapes. It contains the following methods.
Method |
Description |
Constructors |
Copy - Creates a duplicate shape from another that already exists;
the newly created shape is independent of the earlier one.
Single-parameter - Creates a shape from a specifying value |
Destructor |
For removing dynamically allocated data components |
operator= |
For assigning the value of one shape to another |
Draw |
Commands the shape to draw itself in a graphical area |
Read |
Reads a shape from a data file |
Write |
Writes the shape to a data file |
Clone |
Allocates a new shape object that is a copy of the current object
and returns a pointer to the new object |
A quick review of the code shows that it is very much a skeleton for what is
needed to truly implement a shape. We will be using the shape class later primarily as
a convenient class type in which to refer to all shapes that have been
derived from this class. As you progress through CS321 you may find it necessary
to add new methods to the shape class or modify the existing ones. For the
purposes of this assignment you shouldn't make any changes to shape.h or
shape.cpp, except for one additional data attribute.
The file image.h contains
part of the declaration for a container class (called image) designed to hold
pointers to
shapes. By having it hold the base class shape pointer we can, in effect, have
it contain any derived class shape pointer as well. Further, if we use the STL
to build our data structure we must remember that it will only be storing copies
of the pointers and not the objects themselves. This means we will be requiring
that all shapes must be allocated on the heap (via new) and that once
added to the container the container will take responsibility for managing and
deleting them in its destructor since the STL object will only delete its copy
of the pointer.
The methods for the image class are listed below.
Method |
Description |
Constructors |
No-argument - creates a default image (empty, containing no
shapes)
Copy - Creates a duplicate image from one that already exists; the
newly-created image contains deep copies of the earlier image's
shapes |
Destructor |
Delete all the shape objects from the container (the STL destructor will
not do this automatically since it only holds pointers to the shapes) and delete all other dynamic objects. |
operator= |
Assigns an image to contain the same shapes as another image. The
image object must first completely destroy it's current
collection of shapes, and then create deep copies of the shapes
it is being assigned from the other image. |
Add |
Add a new shape (via a pointer) to the container |
ReDraw |
Ask all the shapes to draw themselves by iterating through the
image's collection of shapes, and invoking each shape's Draw() method
via the pointer |
Write |
Output all the shapes to a data file |
Read |
Read a set of shapes from a data file |
Zoom |
Zoom in on an image |
Unzoom |
Zoom out on an image |
Reset |
Revert an image to its "original" state |
Erase |
Remove all the shapes from an image and return any dynamic memory
they occupy |
By organizing things within a container class it will make it easier for
other objects to conveniently manipulate an image. Again the methods are defined
in a rather sketchy form since we as yet do not know exactly what information
will be needed to implement the various methods in a real graphic environment.
You can expect to add to and modify this class throughout the term. Note
that the definition of appropriate data members has been left entirely up to you. One of the goals
of this assignment is to have you choose an appropriate
STL type for
storing shapes. You may find that there are better choices than the vector
or list.
Of all these methods the Read method may turn out to be the most
difficult to implement. In most cases C++ can automatically pick the proper
virtual method for derived class objects. All of this presupposes that the type
of object is already known and created, but when reading an object from a file or
the terminal we do not know what kind of object we have until after we begin
reading it. This presents somewhat of a dilemma. There have been a number of
suggestions on how to solve this particular problem and often revolve around a
special set of classes called
factory or foundry classes. This is a nice abstract way to do this, but you
may be able to approach the problem in a simpler fashion. Essentially each shape
should contain a type field (see the UML diagram below) which should always appear first in the data file. The Read
method in this case will have to first input the type field and then use a
construct like switch-case to determine which type of shape it is to create and then
input the remaining data. This requires that the container class Read method be updated
every time you create a new kind of shape. (Typically a factory class's
job would be to do this for the container class and thus isolate the container
class from this level of detail.)
Lab Activity
For this lab assignment, follow the instructions below. The green instructions are required to get an "A"-level
grade; otherwise, the maximum grade you'll be able to achieve for this
assignment will be a "B".
-
You will find it useful to use a qmake
project file to facilitate compiling and linking. Copy lab1.pro from lab1 to
your local working directory for lab 2 (renaming it lab2.pro), make the
necessary changes to use the files for the current lab. Compile and link your program using the commands
qmake
lab2 and make. Use
this cs321lab2.cpp file as your main program and
the basis for writing tests for your classes.
- Derive at least two different classes from the shape class.
(point and line are two mandatory ones). Derive a third class such as
rectangle, ellipse, circle, etc).
- Have each
derived shape contain different data members (that parametrically define the object).
- Implement an additional protected static
attribute in the shape class; it will automatically be inherited by each
derived shape. Use this attribute as a counter that auto-increments
every time a derived-shape constructor creates a new derived shape.
Implement all of the member functions.
- For this lab, implement each derived shape's virtual Draw()
method such that a simple message is sent to std::cout; e.g. "drawing a
point <x,y> #x",
where <x,y> is the actual coordinate of the point;
x is the auto-incremented counter.
- Have the derived shape's destructor send a message to cout such as
"destroying point<x,y>"
Sketch the UML class diagrams and show me the code for these classes to me before the end of lab.
- Implement the image container class.
- For this lab, have
Zoom(),
Unzoom(),
and Reset() do nothing.
- Implement a data member of image that can contain a name for the
image, such as "car", "house", etc.
- Have the Redraw() method of the image
object call the Draw() method for each object derived from shape.
That is, you must iterate through the collection class object owned by the
image object, visiting each contained shape, and invoke the
shape's virtual Draw() method via the contained shape pointer. The magic of
virtual functions invokes the correct Draw() method of the underlying
object.
Show me the code for this class before the end of lab.
- Develop a driver program for testing purposes. The driver program should
- be able to create multiple image
objects (so you can thoroughly test copy and =) and add an arbitrary
number of the derived shapes to the container.
- list all images by name, and the number of
shapes in each
You must be able to demonstrate at least
the preceding two features during the 2nd week of this lab.
- be able to make a new copy of an existing image (via the Clone
method). The copied image's name should be "copy of XXX", where XXX is
the original image's name
- be able to assign the contents of one image to another. The contents
of the target image must be destroyed correctly. The target image's name
should be "duplicate of XXX", where XXX is the source image's name.
- be able to write an arbitrary image to file.
- after closing your program and restarting it, be able to read an
image from a file.
- Using Enterprise Architect, complete the UML class diagram above,
including all attributes and operations in all of the classes.
Demonstrate at least the incomplete diagram above as
an EA model during the 2nd week of this lab.
Thoroughly test all of the methods in the image class to make sure they work correctly. Pay
special attention to the cases of when you copy an image (either through the
copy constructor or operator=) and then destroy
the original, because if you don't correctly implement deep copying, you'll
see big problems. This will help test your Clone() methods; i.e., to make sure
you didn't just copy a pointer (shallow copy), but rather copied the entire object
(deep copy).
Lab report and submission (due 11:00 A.M. day of Lab 3)
This is a 2 week lab. You must demonstrate the first two
features indicated above during the 2nd lab period. During lab 3, you must
demonstrate your finished working program as well as your completed EA model.
You must submit a quality lab report along with your program. Use
this Word document as a template for your
report, carefully following the instructions within. Note that you may be able
to use OpenOffice (under Kubuntu) to edit this document and use file sharing to
transfer the report and your source files to Windows.
Submit your assignment following these instructions:
-
Navigate to
your project directory where you have your source files. Besides your
report, I only want the .cpp, .h, and .pro files.
-
Use webCT to
submit your
assignment ("Lab 2").
-
Record the time (in minutes) you spent on
this lab in the FAST system for weeks 2 and
3.
Make sure you make an additional entry for last week's lab while you're at
it.
Be sure to keep copies of all your files, in case something gets lost.Your lab grade will be determined by the following
factors:
Program quality
- Meeting requirements (correct behavior and output)
- Technical quality (comments, naming etc).
- Program clarity (formatting, spacing etc)
Report quality
- Required content
- Spelling and grammar (your word-processing program has
a built-in spell checker
- use it)
- Timeliness of submission as stated in the
course policies.
If you have any questions, consult me.
Glossary
You may find some of these terms useful in reviewing derived classes and
inheritance.
- Abstract Class - A type of class with pure virtual member
functions that behaves as a base class but prohibits the instantiation of
any members of that class. It is used to take advantage of inheritance yet
prohibiting the generation of objects that are not completely defined.
- Base Class - A class from which other classes are derived. It
usually provides a more generalized framework of data members and functions
that are needed by all similar object.
- Derived Class - A class that is obtained by inheriting data
members and functions from a base class. A derived class usually adds
specialization to a base class.
- Inheritance - A mechanism where a derived class object also has
the same data members and functions as an object from the base class from
which the derived class was derived. NOTE: Constructors, destructors, and
overloaded functions are not inherited. Additionally the base class
constructor is always called before the derived class constructor and the
base class destructor is always called after the derived class destructor.
- Overloading - The creation of multiple functions with the same
name yet dissimilar calling arguments (signature). This allows for the reuse
of an appropriate name for multiple circumstances.
- Overriding - When a derived class declares a member with the same
name (the same signature is not required) the base class member is
overridden and cannot be called directly from the derived class. This is
common with virtual functions, but is not restricted to them. Care should be
taken though when upcasting since a non-virtual overridden function will
result in the base class version being called.
- Polymorphism - The mechanism where an upcast version of a derived
class object automatically finds the versions of member functions that are
appropriate for the derived class.
- Private - A designation for class members so that they are only
accessible from member functions of that class or friends of that class.
- Protected - A designation for class members so that they are only
accessible from member functions of that class, member functions of derived
classes, and friends of that class.
- Public - A designation for class members so that they are
accessible by anyone.
- Pure Virtual - Any virtual member function in a base class which
is declared =0 is said to not exist. (i.e. no definition will be provided.)
Any class with pure virtual data members is an abstract class.
- Slicing - When derived class objects are passed to functions and
returned from functions as objects of the base class the extra derived class
members are removed. This can be avoided by only passing derived class
objects as pointers or as references. This way the entire object is passed
not just the base class components. Due to limitations of the STL we will be
using pointers to avoid this.
- Static - A designation for class members that indicate that this
member is shared by all objects of that class.
- Upcasting - The action of referring to a derived class object as
if it were an object of a base class. When doing this be sure to avoid
slicing.
- Virtual - A designation for class members that indicates that
when a member of an upcast version of derived class object is accessed it
uses the derived class version not the base class version.
This lab was originally developed by Prof. Henry Welch.
This page was last updated on
09/26/2006.