CLASSES Aims
To enable
students to: ·
use
the struct and class constructs ·
describe
data and function members of a class ·
access
class members ·
construct
and destruct classes ·
understand
this Use the struct and class constructs
In the
first handout classes were described as being a way of grouping data and the
code that operates on it into one logical place. In order to achieve this C++
requires a way of describing the data and functions that may be held by a
class. C used the
concept of a struct to group several data items (basic
and/or user defined) into a single user defined data type. For example a point
on a screen may be defined in terms of it offset horizontally (x) and
vertically (y) from the top left hand corner of the screen. A struct may be defined to handle this
information thus: struct
locn { int m_x; int m_y; }; Many
programmers follow the convention of preceding data members with an m_. Under C++
structures have been extended to include functions, i.e. a structure can now
include both data items and functions to manipulate them. The locn structure above can now include a member
function to set the values of x and y: struct
locn { int m_x; int m_y;
void set (int, int); }; C++ also
introduces a new construct, class.
Essentially struct and class are the same, they only differ in their default access
rights with members of struct being public by default and those of class, private. The example struct can be represented by the following
class: class
Clocn { public: int m_x; int m_y;
void set (int, int); }; Note, the
name has changed form locn to Clocn. Classes under C++ follow the same naming convention as
variables (i.e. there are few restrictions), but it is common for class names
to be preceded by an upper case C to indicate the name represents a class. Access
rights will be discussed later in this handout. By
convention, a struct is used where only data is
aggregated (i.e. there are no manipulation functions) while a class is used where data is aggregated
and has associated manipulation functions. In our Clocn example, the early examples without
the set function would be represented by a struct, once the function is introduced a class should be employed. Having
defined Clocn in a header file it is necessary to
implement its member function ina .cpp file. #include
“locn.h”// Include the file were the
class Clocn prototype is defined void
Clocn::set (int x, int y) { m_x = x; m_y = y; } Member
variables, such as m_x and m_y , appear to member functions as global variables. Changes
made in one function to a variable are visible to other functions - there is no
need to pass values around via parameters. After
pulling in the prototype of the class (contained in the file locn.h - although it could also be placed
directly in the .cpp file) the member function set is defined. This is achieved by entering the name of the
class followed by the scope resolution operator (::) and the name of the function. This convoluted syntax
allows the compiler to determine which function definition we are providing -
remember different classes can implement functions of the same name (polymorphism). Having
defined the class Clocn it is now possible to use it.
Classes are treated by the compiler as a new data type (e.g. char, float, int, etc.) so they are declared in the
same fashion. The following declares an object position of type Clocn: void
main () { Clocn position; } Variables
of this new type can now have their member functions called: position.set
(10, 20); The Clocn class is what is termed a base
class - it is not derived from another. It could be that the location class
needs to inherit behaviour from another, for example the colour an item at the
specified location on the screen should be displayed in. Assuming a class
called Ccolour exists, it is possible tomodify the definition of the class Clocn to inherit the behaviour defined by
the class Ccolour, thus: class
Ccolour { protected: int m_iColour; public: void SetColour (int); }; class
Clocn: public Ccolour { public: int m_x; int m_y;
void set (int, int); }; Objects of
type Clocn now have the ability to set their
colour via the SetColour function as in: Clocn
position; position.SetColour
(7); Describe data and
function members of a class
Classes are
used to encapsulate data items and their manipulation functions. There is no
restriction on the types of data a class can contain, it may be simple data
types such as int or char or other more complex ones such as structs and classes -
whatever is appropriate for the item being modelled. To
reiterate, a data member of a class is a variable stored within the class, in
the Clocn example m_x and m_y are data members. A function member is a function contained
within the class, the Clocn class contains the member function set. Access class
members
In order
for a class to be useful it must be able to responds to messages. A message
flow is indicated by the access of a data or function member. Two methods exist
for accessing the items contained within a class. The . member
access operator
allows a data or function member of the class to be accessed and manipulated.
This syntax permits the compiler to determine which object is being manipulated
- objects (even those of the same class) are independent entities with their
own data values, hence the compiler needs to know which object is beingreferenced. The . operator will only work against
objects, as in the following: #include
“locn.h”// Include the file were the
class Clocn prototype is defined void
main () { Clocn position; position.set (10, 20); } or position.m_x
= 10; position.m_y
= 2; When
dealing with pointers to objects the access operator is ->. For example in the following
program a function is used to manipulate the contents of an object. In order
for vManipulatePosition to be able to manipulate the local
variable position it must be provided with a pointer
to it. #include
“locn.h”// Include the file were the
class Clocn prototype is defined void
vManipulatePosition (Clocn *pLocn) { pLocn->set (10, 20); } void
main () { Clocn position; vManipulatePosition (&position); } Besides
being used in tying function definitions to classes, the scope resolution operator :: may be used to specify a specific
variable or function. Without qualification the :: permits access to a globally defined function or variable,
as in: #include
<iostream.h> int
amount = 123; // A global variable void
main() { int amount = 456; // A local variable cout << ::amount; // Print the global
variable cout << '\n'; cout << amount; // Print the local
variable } It can also
be used to modify which function in a class hierarchy is executed. In the
following example the function f is
defined in three places, each one is execute in turn. void
f(int i)// global function { … } class
A// base class { public: void f (int i); } class
B: public A// derived class { public: void f (int i); } void
main () { A *a; B *b; …// create objects ::f;// calls global function a->f(1);// calls function f in class A b->f(1);// calls function f in class B b->A::f(1);// calls function f in class A } In order
for an object to be able to police its state effectively it needs to be able to
control access to the data it contains. A common practice is to hide all data
members within the object and only allow access to a few, selected member
functions - without such policies it is possible for unexpected behaviour to
take place. For example a class representing a current account may have methods
to increment and decrement the amount of money held, with the decrement
function issuing a warning letter whenever it is called and the balance is
negative. If other parts of the system can manipulate the member variable
holding the balance without going through the decrement function it is possible
that the warning letter is never issued. To control access to member functions
and data C++ uses the keywords private, protected and public. For example
the Clocn class may have a function, bVerify, that checks the supplied position
is valid (i.e. greater than 0 in both the x and y planes). This function, together
with the member variables should be private to the class - i.e. other parts of the program can not
access or change them. The following class definition will provide these
features: class
Clocn { private: int m_x; int m_y; bool bVerify (int, int);
public: void set (int, int); }; The public keyword permits free access to the
following variables and functions. Any item of code may change them at will.
This section stays in effect until the protected, or private keyword is met. The private keyword prevents code outside of
the class -including those that derive
from it - from accessing the variables or functions within that section. Again
it stays in effect until the protected
or public keyword is encountered. The protected keyword is similar in action to the
private, except that classes derived from
it have free access to the member functions and variables. Again the effect of
this keyword stays in effect until the public or private keyword is encountered. By default
all items within a class are private. Construct and
destruct classes
A constructor
is a special function called by the compiler whenever an object is created or
defined. Its purpose is to put the object into an executable state - initialise
variables, load data from files, etc. If the programmer does not provide a
constructor function, the compiler will provide a default implementation -
which unfortunately does nothing. A class may
have multiple constructors, but they must be all of the same name as the class.
They can take any number of parameters, and as with all functions default their
values, but return nothing. Constructors are declared as: class
Clocn { private: int m_x; int m_y; bool bVerify (int, int);
public: Clocn (); Clocn (int, int); void set (int, int); }; In the
above example we have two constructors, one taking no parameters and the other
two. Note, no return type is indicated - not even void. The
constructors are defined as follows: Clocn::Clocn
() { m_x = 0; m_y = 0; } Clocn::Clocn
(int x, int y) { m_x = x; m_y = y; } The first
initialises the member variables to 0, while the second sets them to whatever
values are provided. A
constructor with no parameters is called whenever an instance of the object is
declared without parameters, as in: void
main () { Clocn position; } If two
integers parameters are provided at object creation, as in the example below,
the second constructor is called. In this example if any other combination of
parameters are provided a compilation error will ensue. void
main () { Clocn position (10, 20); } In order to
free up any resources used by the object during its lifetime the compiler will call
a destructor function whenever it is destroyed. As with constructors, an
empty destructor function will be provided by the compiler if none is written
by the programmer. Unlike constructors, only one destructor can exist, which
takes no parameters and returns no values. A destructor is identified in the
same way as a constructor, but with a tilde (~) in front of its name, as in: class
Clocn { private: int m_x; int m_y; bool bVerify (int, int);
public: Clocn (); Clocn (int, int); ~Clocn (); void set (int, int); }; Understand this
The
variable this is a constant pointer, local to a
member function that is automatically provided by the compiler - developers can
not declare a variable of this name or assign values to it. It is used by the
compiler to point to the instance of an object to which the member function belongs. Whenever a
member function is called the compiler adds a hidden argument to the function
that contains the address of this object. For example, the following function
call: Clocn
position; position.set
(10, 10); can be
interpreted as: set
(&position, 10, 10); The hidden
argument is available for use within the function as the this pointer, as in: void
Clocn::set (int x, int y) { // These statements are equivalent m_x = x; m_y = y; this->m_x = x; this->m_y = y; } If not
mentioned explicitly, the this
pointer is used implicitly when manipulating member variables of an object from
within a member function. For example, the set function of the Clocn class is defined as: void
Clocn::set (int x, int y) { m_x = x; m_y
= y; } but is
interpreted by the compiler as: void
Clocn::set (int x, int y) { this->m_x = x; this->m_y = y; } |
|
||
|
|||
|
Last updated: 11th July 2006. copyright © 2006 Greystoke Systems Ltd. Web address: http://www.gsys.biz/Documents/Services/Tuition/CityAndGuilds/7261-249/Classes.htm |