Topic 7: C++ Classes
Synopsis
Classes¤
-
The fundamental unit of data type abstraction¤
and encapsulation¤
-
A class¤
specifies how the memory used by objects is laid-out (just like a C struct
does)
-
Also specifies which functions are allowed to access parts of that memory
(unlike a C struct)
Declaring classes¤
-
Consider a C struct and associated functions:
/* C VERSION OF Vehicle ADT */
struct VehicleRec
{
int myYear;
char* myOwner;
char* myRegNum;
};
typedef struct VehicleRec Vehicle;
void v_Init_void(Vehicle* v);
void v_Init_data(Vehicle* v, char* regnum, int year);
void v_Cleanup(Vehicle* v);
void v_RegisterTo(Vehicle* v, char* name);
void v_CheckRecord(Vehicle* v, char* name);
In C++, we could create an equivalent (but more powerful) ADT by declaring
a class¤:
class Vehicle
{
public:
Vehicle(void);
Vehicle( char* regnum, int year);
~Vehicle(void);
void RegisterTo(char* name);
private:
int myYear;
char* myOwner;
char* myRegNum;
void CheckRecord(char* name);
};
Note that the data fields (members¤)
and the associated functions are declared together, all in the class¤.
Class¤
data members¤¤
-
Can be of any inbuilt or user-defined type
-
Declared and used just like the fields in a struct
-
Except that they are usually not accessible to any function which
has access to a Vehicle object!
-
Store the "state¤"
of an instance¤
of the class¤
Class¤
function members¤¤
-
Are functions which can only be called for objects of a particular class¤
-
Are usually accessible anywhere that their object is available
-
Represent the "behaviour¤"
of an instance¤
of a class¤
-
Because all functions are defined for the class¤
as a whole every instance¤
of that class¤
has the same behaviour¤
-
May be only declared in the class¤
(as above), or may be fully defined in the class¤:
class Vehicle
{
public:
Vehicle(void);
Vehicle( char* regnum, int year);
~Vehicle(void);
void RegisterTo(char* name)
{
CheckRecord(name);
myOwner = name;
}
private:
int myYear;
char* myOwner;
char* myRegNum;
void CheckRecord(char* name);
};
If not fully defined in the class¤
declaration, must be defined somewhere else (usually in a .C code file):
void Vehicle::CheckRecord(char* name)
{
if (wantedFelon(name))
{
cerr << "There are outstanding warrants on "
<< name << endl
<< "Do not allow this person to register "
<< myRegNum << "!" << endl;
}
}
Member¤
access control
-
The public: and private: keywords specify
what kind of access is available to the various members¤
of the class¤
-
Members¤
declared in the public: section can be accessed anywhere
(just like the members¤
of a C struct)
-
But members¤
declared in the private: section can only be accessed by
other class¤
member¤
functions¤
-
You can think of the access like this:
-
Usually put all data members¤¤
in the private: section (encapsulation¤)
and most function members¤¤
in the public: section
-
A class¤
can have multiple public: and private:
sections. The current accessibility¤
is determined by the immediately preceding keyword
-
There's a third (intermediate) level of accessibility¤
(protected:), which we'll discuss when we look at C++ inheritance
Creating objects of
a given class¤
-
Each class¤
declaration creates a new type (just like the C "struct-then-typedef"
approach)
-
Can create instances¤
(objects) of that new type with the normal variable declaration or dynamic
allocation syntaxes:
#include "Vehicle.h"
Vehicle gv1; // GLOBAL Vehicle OBJECT
int main(void)
{
Vehicle lv1; // LOCAL Vehicle OBJECT
Vehicle lv2; // LOCAL Vehicle OBJECT
Vehicle* vptr = new Vehicle; // DYNAMIC ALLOCATION
// ETC...
}
Like ordinary C variables, non-dynamically-allocated objects cease to exist
at the end of the scope in which they are declared
Accessing class¤
members¤
-
Access to all accessible class¤
members¤
(including member¤
functions¤!)
of an object is via the '.' operator
#include "Vehicle.h"
int main(void)
{
Vehicle lotusEsprit;
lotusEsprit.RegisterTo("Jon McCormack"); // DREAM ON!
}
However, if we have a pointer to an object, we can use the '->' notation:
#include "Vehicle.h"
int main(void)
{
Vehicle* car;
car = new Vehicle;
car->RegisterTo("Jon McCormack");
}
Constructors¤
-
In C (and C++), inbuilt types and normal structs can be initialized:
/* NORMAL C INITIALIZATIONS */
int cost = 250000;
struct StudRec sr = { "Lee", 12345678, 99.9 };
In C++ we can associate a function with each class¤
which allows us to initialize objects of that class¤
when they are constructed (like we provided v_Init_void()
and v_Init_data() in the original C version)
Such functions are called constructors¤
A class¤'s
constructors¤
have the same name as the class¤,
and don't specify a return type (not even void!)
Like other member¤
functions¤.
they may be defined in the class¤
declaration, or later:
class Vehicle
{
public:
Vehicle(void) // CONSTRUCTOR DEFINITION
{
myYear = -1;
myOwner = 0;
myRegNum = 0;
}
Vehicle(char* regnum, int year); // CONSTRUCTOR
// DECLARATION
// ETC. AS BEFORE
private:
int myYear;
char* myOwner;
char* myRegNum;
};
Vehicle::Vehicle(char* regnum, int year)
{
myYear = year;
myOwner = 0;
myRegNum = new char[strlen(regnum)+1];
strcpy(myRegNum, regnum);
}
Note that the contructors, being members¤
of the class¤,
have access to the data members¤¤,
even though the data members¤¤
are private
When one or more constructors¤
are defined for a class¤,
one of them is called whenever an object of that class¤
is created
The constructor¤
with a void parameter list is called if the object is created without any
useful information being provided:
Vehicle v; // CALLS v.Vehicle()
Vehicle* vptr = new Vehicle; // CALLS vptr->Vehicle()
However, if a set of arguments is given after the declaration (or the call
to new), then the constructor¤
with the corresponding parameter list is called:
// THESE CALL Vehicle(char*,int) CTOR
Vehicle v ("DMC-042", 1978);
// CALLS v.Vehicle("DMC-042",1978)
Vehicle* vptr = new Vehicle ("AAA-111", 1992);
// CALLS vptr->Vehicle("AAA-111",1992)
Note that the multiple constructors¤
are an example of function overloading (same name, different signatures¤)
Destructors¤
-
Sometimes it's also useful to do something when an object ceases to exist
(i.e. when it goes out of scope, or is deallocated using delete).
Why?
-
C++ provides a means of specifying a function which is called automatically
whenever a object is about to cease to exist
-
Such a function is called a destructor¤,
and always takes the name of its associated class¤
preceded by a '~' For example:
class Vehicle
{
public:
// CTORS WOULD BE HERE
~Vehicle(void) // DESTRUCTOR
{
delete[] myOwner;
delete[] myRegNum;
}
// ETC. AS BEFORE
private:
int myYear;
char* myOwner;
char* myRegNum;
};
Destructor¤
is called when a locally-declared object goes out of scope, or when a dynamically-allocated
object is deleted, or for a global object at the end of the program:
Vehicle gv1;
int main(void)
{
Vehicle lv1;
Vehicle lv2;
Vehicle* vptr1 = new Vehicle;
Vehicle* vptr2 = new Vehicle;
delete vptr2; // vptr2->~Vehicle() CALLED DURING delete
} // lv2.~Vehicle() AND lv1.~Vehicle() CALLED AT THIS POINT
// [END OF FILE]
// gv1.~Vehicle() CALLED AT THIS POINT
Where is the destructor¤
for the object pointed to by vptr1 called?
There can be only one destructor¤
per class¤.
Why?
The this pointer
-
Recall that each of the v_... functions in the original
C version took a Vehicle* as their first argument. Why?
-
The corresponding C++ class¤
members¤
didn't need a pointer to the object on which they operated. Why?
-
However, such a pointer is available to those member¤
functions¤,
should they require it.
-
It's called: this
-
Hence we could rewrite the various member¤
functions¤:
class Vehicle
{
public:
Vehicle(void);
Vehicle( char* regnum, int year)
{
this->myYear = year;
this->myOwner = 0;
this->myRegNum = regnum;
}
~Vehicle(void);
void RegisterTo(char* name)
{
this->CheckRecord(name);
this->myOwner = name;
}
private:
int myYear;
char* myOwner;
char* myRegNum;
void CheckRecord(char* name);
};
void Vehicle::CheckRecord(char* name)
{
if (wantedFelon(name))
{
cerr << "There are outstanding warrants on "
<< name << endl
<< "Do not allow this person to register "
<< this->myRegNum << "!" << endl;
}
}
For members¤
of the Vehicle class¤
it's a Vehicle * const (not a const Vehicle*)
Reading
- Lippman & Lajoie
- New: Chapter 13.
- Stroustrup
- New: Chapter 10.
This material is part of the CSE2305 - Object-Oriented
Software Engineering course.
Copyright © Jon McCormack & Damian Conway, 1998–2005. All rights
reserved.