Computer Science II - Lab 3: Classes

Objectives:

  • To introduce the C++ struct
  • To learn how to create objects using the C++ class
  • To practice creating C++ objects
  • To learn how to work with multiple files

Instructions:

  • Create a word processing document as an answer sheet
  • Put all answers, and code, on the answersheet, numbering it as numbered below.
  • Turn the answer sheet into Moodle.
The C++ Class
C++ has an additional structured data type called the class. It is very similar to the C++ struct. The main difference being that the struct data type usually contains only a collection data members while the class data type usually contains a collection of data members and function members (methods) that manipulate the data contained in the class.

The C++ class provides a mechanism for programmers to create objects. A software object is a self-contained computing entity which has its own data and its own associated operations called methods. We say that the C++ class supports data encapsulation (combining data and functions on that data into a single program unit). In real life, we deal with physical objects continually. When we sit in a chair (an object) at our desk (an object), we might consult our calendar (an object) to plan our activities for the day. Think of an object that we experience and use daily, say an automobile. The automobile object has data or data features (a steering wheel, an ignition, is either straight shift or automatic, etc.) and it has certain functions or operations which it should perform. Some of the functions include: move forward, move backward, start motor, stop motor, turn lights on, etc.

Objects have a public interface and a private component. The public interface of an object is composed of the collection of functions and features which are available to the user to allow effective use of the object. For example, the public interface of the “automobile” object would include buttons on the dashboard to allow the user to control certain operations like turning on the lights and allowing the user to use a key to start the motor. Private data or functions are unavailable to the public. This would be like disabling the hood release so that the user cannot access the internal parts of an automobile (which would be a good idea in some cases). Normally, private data and functions are those data and instructions that should not be changed by the user.

Suppose that we wish to create a book object. Consider the following record structure:

struct BookRec
{
   string title;
   string author;
   string publisher;
   float cost;
   DateRec publishDate;
}

The following table indicates possible functions that we might want to include in this object.

Function Name

Function Purpose

PrintBook

Prints out the data contained in a book object

ChangeCost

Changes the cost of a book

IsAvailable

Determines whether the book is currently available

A large portion of a programmer's job is to design objects and then use the C++ class mechanism to actually define the object and determine how the object is to be manipulated. To design the object, the programmer first thinks of the type of data that is needed to form the object. Secondly, the programmer determines what functions are needed to manipulate the object. The C++ class allows a programmer to create this new type. There are many benefits for creating objects using the C++ class. Some of these benefits include data encapsulation, data security and information hiding. Information hiding refers to the process of hiding implementation details from the users (called clients) of the created class or object. The C++ class can be set up to “hide” implementation details from the user and can restrict the user from seeing how the data is actually stored. This promotes data security since the user cannot “see” the data contained in the object and thus, cannot manipulate the data erroneously. The C++ class promotes "encapsulation" - the concept of binding the data and functions on the data items into one single program unit.

How do we set up a C++ class?

When creating a class, the programmer should (usually) construct the class in two parts: a specification file and an implementation file. The specification file declares the object ( indicates the data types and functions of the class).The implementation file contains the definitions of the class methods (member functions).We will examine the specification file first.

The C++ class specification file

Suppose that we wish to design a rectangle object. We must decide 1) what data should be included in this object and 2) what functions are needed for this object. The data needed for this object should include a rectangle's dimensions: width and height of the rectangle. Some of the functions needed might include: a function to print a rectangle, a function to compute the area of the rectangle, a function to compute and print the perimeter of a rectangle, a function to draw a rectangle and a function to allow the user to set the length and width of the rectangle. Now we are ready to construct the class. The basic syntax is:

class name
{
public:

     //declare all functions and variables that are available 
     //to the user of the class

private:

     //declare all functions and variables that should not be 
     //available to the user
};

For example, the declaration of the rectangle class may appear as follows:

//FILE:lab2rectangle.h
class RectangleType
{
     public:
         void PrintRectangle();
         float ReturnArea();
         float ReturnPerimeter();
         void SetSize(float L, float H);
 
     private:
         float length;
         float height;
};

The RectangleType class has six members - four member functions (PrintRectangle, ReturnArea , ReturnPerimeter and SetSize ) and two member variables ( length and height). The member variables form the data that is needed to create a rectangle and the member functions allow the user to perform different operations with a rectangle. Note that in this example, clients (users) of this class have access to the member functions but do not have access to the member variables.

All of the rectangle class files have been created for you (lab2rectangle.h, lab2rectangle.cpp, lab2main.cpp ).


Exercise 1:

Design a class called CircleType and then create the class specification file called, lab2circle.h, which contains functions and data members similar to the functions and data members contained in the RectangleType class. NOTE: Data members are generally, but not always, placed in the private portion of the class. What data members will be needed for a circle?(radius)


The C++ Implementation File
Next, the programmer should build the implementation file. The implementation file for the rectangle object should contain the function definitions for all functions that were declared in the specification file, lab2rectangle.h. IMPORTANT: Note the following:

  1. Functions in the implementation file have access to the private data members of the class!
  2. The scope resolution operation, "::", is always used in the function header. This informs the compiler to what class the member function belongs.


Exercise 2:

Write the implementation file for the circle class and save it using the name lab2circle.cpp. Note:The area of a circle is pi*r*r where pi=3.1415 and r is the radius of a circle. The perimeter (circumference) of a circle is found using the formula, 2 * pi * r.


Now, how do we use the rectangle class that we have just created? Consider the following example of a client file (a file that declares and manipulates objects of a particular class).

Note that the dot operator is used to invoke member functions.This is how “messages” are passed to an object in C++. Thus,
aRectangle.SetSize(5, 10);
sends the SetSize message to the object called aRectangle. Note the assignment statement after the cout statements. This line is valid and copies all data fields from the object, aRectangle, to the object, bRectangle because we have simple data types making up the two objects.

Note that nowhere in our main function do we try to access the data variables, length and height, which were defined in the RectangleType class. In fact, we could not do so because these data members were placed in the private section of the class. This means that only members of the class can access them.

Compiling and Linking with Multiple Files


Exercise 3:

The above example is a very simple example of a main program that utilizes the rectangle class. All of the rectangle class files have been created for you (lab2rectangle.h, lab2rectangle.cpp, lab2main.cpp ). Copy these files to a Visual Studio Project. Both the main function and the implementation functions for the rectangle class must be compiled. Since they are in separate files, you will need to first use the project menu item to "Add an item...".

Compile and run this program to verify that the program runs properly - Do not submit this run for grading.

Exercise 4:

You have created two additional files, lab2circle.h and lab2circle.cpp.Edit the lab2.cpp file so that it performs the following activities. IMPORTANT: You must tell the compiler to include your lab2circle.h file ---Use: #include "lab2circle.h"

Exercise 5:

Compile your program and turn in a script containing a cat of the files (lab2circle.h, lab2circle.cpp, lab2b.cpp), a compile and a run of this program.

The Fraction Class

Next consider the Fraction class discussed in class:

//*******************************************************

// Implementation file fraction.cpp implements the member 
// functions of class Fraction.

// Header file fraction.h declares class Fraction.
class Fraction
// Class Fraction represents the numerator and
// denominator of a fraction.
{
public:
 
 // Constructors
 Fraction();
 // Post: Numerator and denominator have been set to zero
 Fraction(int Numerator, int Denominator);
 // Post: Numerator has been set to initNumerator;
 //      denominator has been set to initDenominator. 
 Fraction Add(Fraction frac1) const;
 // Post: self + frac1 is returned.
 Fraction Subtract(Fraction frac1) const;
 // Post: self - frac1 is returned.
 Fraction Multiply(Fraction frac1) const;
 // Post: self * frac1 is returned.
 Fraction Divide(Fraction frac1) const;
 // Post: self / frac1 is returned.
 int GetNumerator() const;
 // Post: Numerator of frac1 is returned.
 int GetDenominator() const;
 // Post: Denominator of frac1 is returned.
 void Print() const;

private:
 int numerator;
 int denominator;
};

This class is designed to simulate a fraction data type. THe operations are to return a reduced form, e.g. 1/2 rather than 2/4. For this you will need to compute the greatest common denominator of a pair of numbers. Below is a recursive solution to this problem:

int gcd(int a, int b)
{
 if(b == 0)
 {
    return a;
 } else 
 {
    return gcd(b, a % b);
 }
}

You can use this function to find the GCD, and then divide both the numerator and denominator by the same value.

Below is a partial implementation, as seen in class:

//*******************************************************

// Implementation file fraction.cpp implements the member 
// functions of class Fraction.

#include <iostream>
#include "fraction.h"
using namespace std;

Fraction::Fraction()
{
 numerator = 1;
 denominator = 1;
}

Fraction::Fraction(int Numerator, int Denominator)
{
   numerator = Numerator;
   denominator = Denominator;

}

Fraction Fraction::Add(Fraction frac1) const
// Pre: frac1 and self have been initialized.
// Post: frac1 + self is returned in reduced form.
{
   frac1.numerator = (numerator*frac1.denominator + frac1.numerator*denominator);
   frac1.denominator *= denominator;

   return frac1;
}

Fraction Fraction::Subtract(Fraction frac1) const
// Pre: frac1 and self have been initialized.
// Post: self - frac1 is returned in reduced form.are 
{
   frac1.numerator = (numerator*frac1.denominator - frac1.numerator*denominator);
   frac1.denominator *= denominator;
 return frac1;
}

Fraction Fraction::Multiply(Fraction frac1) const
// Pre: frac1 and self have been initialized.
// Post: self - frac1 is returned in reduced form.are 

{
 return frac1;
}   

Fraction Fraction::Divide(Fraction frac1) const
// Pre: frac1 and self have been initialized.
// Post: self - frac1 is returned in reduced form.are 

{
 return frac1;
}   

int Fraction::GetNumerator() const
{
 return numerator;
}   

int Fraction::GetDenominator() const
{
 return denominator;
}   

void Fraction::Print() const
{
   cout << numerator << "/" << denominator;
}

Finally below is a simple test program for Fraction:

#include <iostream>
#include "fraction.h"
using namespace std;

void main() {
   char wait;

   Fraction x(2,3), y(4,9), z;

   cout << "x: ";
   x.Print();
   cout << " y: ";
   y.Print();
   cout << " z: ";
   z.Print();
   cout << endl;

   z = x.Add(y);

   cout << "x: ";
   x.Print();
   cout << " y: ";
   y.Print();
   cout << " z: ";
   z.Print();
   cout << endl;

   z = x.Subtract(y);

   cout << "x: ";
   x.Print();
   cout << " y: ";
   y.Print();
   cout << " z: ";
   z.Print();
   cout << endl;

   cin >> wait;
}

Exercise 6:

Create a Fractions Project, and add the code from above. Finish the implementation by completing the rest of the functions, and extending the test driver to test ALL of these functions. Include your code, and the output, in your writeup.

Overloading Arithmatic operators

The above Fraction operators are useful, but somewhat cumbersome. We are more used to using +, -, * and / operators. These operators are already defined in C++ for integers and floats. Using the same operator for more than one type in called "overloading" the operator. It is semantically attractive, as + "means" add, whatever we are adding, it is just implemented in different ways for different types.

So, when we overload, we define multiple definitions for the same operator, and simply let the C++ compiler "pick" the right implementation based on the data types being operated on.

Consider the following prototype;

Fraction operator+ (Fraction frac1) const;<br />// Post: self + frac1 is returned.

This defines a version of "+" for Fraction. Below is the implementation:

Fraction Fraction::operator+(Fraction frac1) const
// Pre: frac1 and self have been initialized.
// Post: frac1 + self is returned in reduced form.
{
   frac1.numerator = (numerator*frac1.denominator + frac1.numerator*denominator);
   frac1.denominator *= denominator;

   return frac1;
}

Note that there are two objects required for an add, e.g. A + B. consider the following code which uses the "+" operation:

Fraction a(3,5), 
b(5,2), c; 
c = a + b;*

In this example, when we call the Fraction operator+, the "a" object is passes as the first "this" object, while the "b" object is passes as the "frac1" object. In a sense it is as though the following were done:

c = a.operator+(b);

Exercise 7:

Remake the Fractions class, replacing the "Add, Subtract, Multiply, and Divide" operations with +, -, *, / operations. Modify your test program, and include it all in your document.

-- JimSkon - 2011-02-17

Topic attachments
I Attachment Action Size Date Who Comment
Cppcpp lab2main.cpp manage 1.1 K 2011-02-17 - 20:30 JimSkon Rectangle Test program
Cppcpp lab2rectangle.cpp manage 0.7 K 2011-02-17 - 20:31 JimSkon Rectangle implementation program
Hh lab2rectangle.h manage 0.6 K 2011-02-17 - 20:32 JimSkon Rectangle class interface code
Topic revision: r5 - 2011-03-10 - JimSkon
 
This site is powered by the TWiki collaboration platformCopyright &© by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback