LABORATORY 8 Getting classy

advertisement
LABORATORY 8 Getting classy
Objectives
Objects are the basic unit of programming in object-oriented languages like C++. The C++ construct for
defining new types of objects is the class. A class is the “blueprint” from which an object can be created.
This laboratory examines the basic mechanisms of class types by inspecting several different classes.
Key Concepts
_
class construct
Access specification
Information hiding
_ Constructors
_ Encapsulation
_ Inspectors
_ Data members
_ Mutators
_ Facilitators
_
_
GETTING STARTED
Copy the following files to your directory, you will use them for this lab.
Circle.h, Circle.o, Circle.cpp
Complex.h, Complex.o, complex.cpp
BankAccount.h, BankAccount.o, BankAccount.cpp, banking.cpp
Circle Class
Objects are the fundamental units of programming in object-oriented languages such as C++. Objects are
models of information and are implemented as software packages that contain or encapsulate both
attributes and behavior. A data abstraction is a representation of information and the operations to be
performed on it. A fundamental type is a type that the programming language provides. For example, in
C++, the type int and the operations on it are part of the language definition. That is, all C++ compilers
must provide int objects. In addition to the fundamental types, C++ provides several mechanisms to define
other types. These other types are called programmer- or user-defined types. The class construct is the
most important mechanism for defining new types. With this construct, software engineers can define
encapsulated objects. When defining a class, we typically use the information-hiding principle: All
interaction with an object should be constrained to use a well defined interface that allows underlying
object implementation details to be safely ignored.
By following the information-hiding principle, we can create classes that tend to be reliable and easily
reused. To consider the basics of creating a new object type using the class construct, we first examine the
declaration of a programmer-defined—Circle. Whenever one is designing something, it helps to know how
the thing will be used. Our goal in designing the class Circle is to create an object that is both easy to
construct and simple to use. The first thing to do when creating a class-type object is to determine the
attributes of the object. For circle objects, the necessary attributes are, its center of course, its radius. In
C++, the attributes of a class-type object are referred to as the data members. The following are the
declarations of the data members of Circle:
float x;
float y;
float radius;
The second step when creating a class-type object is to determine the messages the object can receive and
the operations that can be performed on the object. This portion of an object is called its behavior
component. The behavior component of a class-type object is a collection of member functions that provide
the ability to send messages to the object requesting it to perform some action. These member functions
provide the public interface to achieve the desired behavior; other member functions are often present to
assist the member functions that are part of the public interface.
For a circle class, we can divide the messages that a circle needs to handle into three sets. One set of
messages directs a Circle object to return an attribute’s value. The second set of messages directs a Circle
object to change the value of an attribute. The third set of messages directs a Circle object to perform a
service.
Messages that return the value of an attribute are called inspectors. For maximum flexibility, our definition
of Circle includes inspectors for all of the data members. Inspectors will have the qualifier const after their
parameter list. The qualifier indicates that the member function does not modify the object. An inspector
name usually begins with Get.
Messages that change or set an attribute are called mutators. Again, to make the Circle as flexible as
possible, we will have mutators for the attributes that control the size and location of a Circle.
There are class member functions that is neither an inspector nor a mutator. The getArea messages tells the
circle to display its area. getArea() is an example of a facilitator. A facilitator performs a service. In
addition to the class member functions that process messages, a class definition will also specify class
constructors.
A constructor is a member function that initializes an object of that class. A constructor has the same name
as the class. The appropriate constructor is invoked automatically when a Circle object is defined. The
following Circle constructor expects an initial value for each of the data members. Unlike other functions, a
constructor does not have a return type.
Circle(float cx, float cy, float rad);
Besides listing the various members, the definition of a class also indicates which parts of a program can
use the members. There are three kinds of access permissions: public, private, and protected. The general
rule is
A public member has unrestricted access.
A private member can be accessed only by other members of the same class.
A protected member does not have unrestricted access. It can be accessed only by other
members of the same class or classes derived from this class.
_
Open the file Circle.h to see the class definition for Circle. It should resemble the following:
#ifndef _CIRCLE_H
#define _CIRCLE_H
class Circle
{
public:
Circle();
Circle(float cx, float cy, float r);
void Set_x(float cx);
void Set_y(float cy);
void Set_radius(float r);
float Get_x();
float Get_y();
float Get_radius();
float Get_Area();
float Get_Perimeter();
bool Overlap(Circle mycircle);
private:
float x;
float y;
float radius;
};
#endif
Examine the code. Explain to your laboratory instructor the purpose of the preprocessor directives ifndef,
define, and endif. (upload your answer to webct) If you are unsure about any aspects of the definition
of class Circle, consult your laboratory instructor before proceeding
_
Modify the program mycircle.cpp the program by defining a second Circle object yourcircle. Have
yourcircle be different in size and position from the Circle object already defined in the program. Make a
second call getArea() to also display the area of yourcircle.
TO COMPILE your program you will need Circle.o – this is the object code for circle.cpp – for now you
do NOT need to change anything in Circle.cpp (the implementation file) or in Circle.h (the declaration file).
Compile as follows
%g++ Circle.o mycircle.cpp –Wall –pedantic –ansi –o circles
%./circles
The member function overlap checks if 2 circles overlap – call this function using mycircle and
yourcircle to see if they overlap. Recompile and test your program.
Upload your modified program to WebCT.
When defining a class, we give the public section first because the members in this section can be used by
anyone. We give the protected section (if it exists) next. The private section comes last.
Open the file circle.cpp.
Observe that the class name and the scope resolution operator precede the member function name. Note
that the scope resolution operator is a double colon (::). The member functions must be identified this way
so that the compiler can tell that they belong to a class. The first function defined in the file is a Circle
constructor. A constructor for a class is invoked when an object of that class is defined.
To verify that the Circle constructor is invoked when a Circle object is defined, add an insertion statement
in Circle’s constructor that inserts the following message to the stream cout:
_
Circle constructor called.
You may wonder why the constructor uses the mutators to set the values of the data members when the
constructor could access the data members directly. The key idea is that all access to the data members
should be through the same interface (i.e., member function). Thus a client user of a class changes the data
members the same way a public member function of the class changes them. Using this strategy means that
we can change the representation of a data member without having to modify every class member function
that changes the value of the data member. Since all access to the data member is through the mutator, we
need to change only the implementation of the mutator. This concept is probably fuzzy because you have
not yet seen enough code to appreciate this very important design strategy. However, as you develop
additional classes and modify existing classes, you will see that always using a mutator to change a data
member value (whether the entity doing the changing is a client user of the class or a member function of
the class) makes software more flexible, easier to maintain, and easier to modify.
Using Classes
Complex Numbers
Open the File Complex.h. This contains the class interface for a class Complex which represents complex
number.
Open the program called complex_numbers.cpp, you will need to complete the implementation. You need
to begin by declaring two Complex object, then accepts values for the real and imaginary parts from the
user and sets up the numbers. Then the program should calculates (using the member functions) and output
the sum of the two complex numbers and the difference between them (subtract the second number from
the first one).
To compile this program you will also need Complex.o and simply compile using
%g++ Complex.o complex_numbers.cpp –o complex –Wall –ansi –pedantic
%./complex
Upload your modified program to WebCT.
Banking
Open the file BankAccount.h. This contains the class interface for a class BankAccount. It looks like this:
//Program to demonstrates the class BankAccount.
#ifndef _BANKACCOUNT_H
#define _BANKACOUNT_H
#include <iostream>
using namespace std;
//Class for a bank account:
class BankAccount
{
public:
BankAccount();
BankAccount(int dollars, int cents, double rate);
//Postcondition: The account balance has been set to
$dollars.cents;
//The interest rate has been set to rate percent.
BankAccount(int dollars, double rate);
//Postcondition: The account balance has been set to $dollars.00.
//The interest rate has been set to rate percent.
void set(int dollars, int cents, double rate);
//Postcondition:The account balance has been set to $dollars.cents;
//The interest rate has been set to rate percent.
void set(int dollars, double rate);
//Postcondition: The account balance has been set to $dollars.00.
//The interest rate has been set to rate percent.
void update( );
//Postcondition: One year of simple interest has been
//added to the account balance.
double get_balance( );
//Returns the current account balance.
double get_rate( );
//Returns the current account interest rate as a percent.
void output(ostream& outs);
//Precondition: If outs is a file output stream, then
//outs has already been connected to a file.
//Postcondition: Account balance and interest rate have
//been written to the stream outs.
private:
double balance;
double interest_rate;
double fraction(double percent);
//Converts a percent to a fraction.
//For example, fraction(50.3) returns 0.503.
};
#endif
Open the program banking.cpp. There are several tasks for you to complete, using the member functions
declared in the interface. Once complete compile and run the program using
%g++ BankAccount.o banking.cpp –Wall –pedantic –ansi –o banking
%./banking
Next open BankAccount.cpp – examine each member function and ensure you know what is going on, if
you have any questions discuss this with your lab instructor.
Finishing up.
There are some questions on WebCT you will need to answer in ADDITION to uploading your modified
programs (just the .cpps) with some sample runs.
The files you need to upload are the updated
mycircle.cpp
complex.cpp
banking.cpp
Thank your Lab Instructor!
Download