03 Classes
03 Classes
1
Structs vs. Classes
Similarities
1. Essentially the same syntax
2. Both are used to model objects with multiple attributes
(characteristics) represented as data members
(also called fields or instance or attribute variables).
2
Differences
3
C Structs vs. C++ Classes (& Structs)
("traditional" vs "OOP")
C++'s structs and classes model objects that have:
Attributes (characteristics) represented as data members
and C structs
Operations (behaviors) represented as function members
(also called methods).
Operations
This leads to a whole new style of programming:
Function
object-oriented. Objects are self-contained, Members
possessing their own operations — commonly Data
called the I can do it myself principle — Members
rather than being passed as a parameter to an
external function that operates on them and sends Attributes
them back.
4
Declaring a Class
Common Forms:
class ClassName
{
// private by default
Declarations of private members
public:
Declarations of public members
};
class ClassName
{
public: //"interface" of class given first
Declarations of public members
private:
Declarations of private members
}; 5
Notes: Operations
Other programmers put the public interface of the class first and the
hidden private details last. (In the text, the latter approach is used.)
Although not commonly done, a class may have several private and
public sections; the keywords private: and public: mark the
beginning of each.
6
Access to Class Members
A particular instance of a class is called an object:
ClassName object_name;
Private members can be accessed only within the class (object)
(except by friend functions to be described later).
Public members can be accessed within or outside the class
(object); to access them outside the class (object), one must use
the dot operator:
object_name.public_member_name
Notes:
1. "my" in data members names is a reminder of internal ("I can do it myself")
perspective.
"Accessors"
2. const at end of Display()'s prototype makes it a const function vs.
"Mutators"
which means it cannot modify any of the data members.
Good idea to protect data members from accidental modification. 9
3. Why make data members private? (p. 91)
"Hidden" data members:
Cannot be accessed outside class
Application programs must interact with an object through its interface
Public member functions control interaction between programs and class.
Application programs need not know about implementation!
Implementation may change (improve storage, simpler algorithms.
etc.)
If interface is constant, programs using an object need not be changed.
Definitions placed outside the class declaration must tell compiler where the
corresponding declaration/prototype is :
13
Implementation of class Time — Version 1
// Time.cpp -- implements the Time member functions
#include "Time.h"
16
Test driver for class Time
int main()
{
Time mealTime;
mealTime.Set(5, 30, 'P');
cout << "We'll be eating at ";
mealTime.Display(cout);
cout << endl;
}
Execution:
We'll be eating at 5:30 P.M. (1730 mil. time)
17
Object-Oriented Perspective
Procedural: Send object off to some function for processing
OOP: Send a message to the object to operate on itself.
To set my digital watch to 5:30 P.M., I don't wrap it up and mail
it off to Casio. I push a button!
To display the time on my watch, I don't wrap it up and mail it
off to Casio and have them tell me what time it is. I have it
display the time itself, perhaps pushing a button to turn on the
backlight so I can see it .
18
Notes:
1. Member functions: "Inside" an object, so don't pass object to them as a
parameter. (They receive the object to be operated on implicitly, rather than
explicitly via a parameter.)
Non-member functions: "Outside" an object, so to operate on an object, they
must receive it via a parameter.
2. Public items must be qualified when referred to outside the class declaration:
ClassName::ItemName
Public constants are usually declared static so they are global class
properties that can be accessed by all objects of that class type rather than each
object having its own copy.
3. Simple member functions: Usually specified as inline functions.
This suggests to compiler to replace a function call with actual code of
the function with parameters replaced by arguments
— saves overhead of function call.
Danger:
"Code Bloat"
19
Two ways to inline a class function member:
23
Class Constructors
Constructing an object consists of:
(1) allocating memory for the object, and
(2) initializing the object.
In our example, after the declaration
Time mealTime;
memory has been allocated for object mealTime and it's data
members have some default (perhaps "garbage") initial values.
Need to:
Specify initial values for mealTime
Provide default values to be used if no initial values are
specified.
This is the role of a class' constructor. 24
(Later, it will also allocate memory.)
Class Constructors - Properties
1. Names are always the same as the class name.
2. Initialize the data members of an object with default values or
with values provided as arguments.
3. Do not return a value; have no return type (not even void).
4. Often simple and can be inlined.
5. Called whenever an object is declared.
6. If no constructor is given in the class, compiler supplies
a default constructor which allocates memory
and initializes it with some default (possibly garbage) value.
A default constructor is one that is used when the declaration of
an object contains no initial values:
ClassName object_name;
An explicit-value constructor is used for declarations with initial values:
ClassName object_name(list-of-init-values);
7. If we supply a class constructor, we must also provide a
default constructor or we can't use first kind of declaration.
25
Constructors for Time class
In Time.h
class Time
{
public:
/* --- Construct a class object (default).
* Precondition: A Time object has been declared.
* Postcondition: Data members initialized to 12, 0, 'A', and 0.
****************************************************************/
Time();
/* --- Construct a class object (explicit values).
* Precondition: A Time object has been declared.
* Receive: Initial values initHours, initMinutes,and
* initAMPM
* Postcondition: Data members initialized to initHours,initMinutes,
* initAMPM, & correspoding military time
**********************************************************/
Time(unsigned initHours, unsigned initMinutes, char
initAMPM);
. . .
// other member function prototypes
private:
/********** Data Members **********/ 26
. . .
Constructors for Time class - Implementation
In Time.h, after class declaration:
inline Time::Time()
{
myHours = 12;
myMinutes = 0;
myAMorPM = 'A';
myMilTime = 0;
}
In Time.cpp:
Time::Time(unsigned initHours, unsigned initMinutes, char initAMPM)
{
// Check class invariant
assert(initHours >= 1 && initHours <= 12 &&
initMinutes >= 0 && initMinutes <= 59 &&
(initAMPM == 'A' || initAMPM == 'P'));
Remember..
myHours = initHours; . exceptions
myMinutes = initMinutes;
myAMorPM = initAMPM;
myMilTime = ToMilitary(initHours, initMinutes, initAMPM); 27
}
Testing #1:
Time mealTime, //default constructor
bedTime(11,30,'P'); //explicit-value constructor
mealTime bedTime
myHours 12 myHours 11
myMinutes 0 myMinutes 30
myAMorPM A myAMorPM P
myMilTime 0 myMilTime 2330
Function members Function members
mealTime.Display(cout); Execution:
cout << endl;
bedTime.Display(cout); 12:00 A.M. (0 mil. time)
cout << endl; 11:30 P.M. (2330 mil. time)
28
Constructors for Time class — Default Arguments
App. 1 from
"Other Links"
Can combine both constructors into a single constructor function by
using default arguments:
Replace constructors in Time.h with:
/*--- Construct a class object.
Precondition: A Time object has been declared.
Receive: Initial values initHours, initMinutes, and
initAMPM (defaults 12, 0, 'A')
Postcondition: Data members initialized to initHours,
initMinutes, initAMPM, & correspoding
military time.
*/
Note: Any parameter with default argument must appear after all
parameters without default arguments. 29
Testing:
Time mealTime, t1(5), t2(5, 30), t3(5, 30, 'P');
Creates 4 Time objects:
12 5 5 5
0 0 3 3
A A 0A 0P
0 500 530 1730
mealTime.Display(cout);
Execution:
cout << endl; 12:00 A.M. (0 mil. time)
t1.Display(cout); cout << endl; 5:00 A.M. (500 mil. time)
t2.Display(cout); cout << endl; 5:30 A.M. (530 mil. time)
t3.Display(cout); cout << endl; 5:30 P.M. (1730 mil. time)
30
Copy Operations
Two default copy operations are provided:
1. Copy in initialization (via copy constructor )
2. Copy in assignment (via assignment operator )
Note: This is OK
for now
Type t = value; Type t;
t = value;
31
Examples: Time t = bedTime;
Time t(bedTime);
Both:
1. Allocate memory for t
2. Copy data members of bedTime so t is a copy of bedTime
11 11
30 30
P P
2330 2330
In contrast:
Time t = Time(11, 30, 'P');
calls the explicit-value constructor to construct a (temporary)
Time object and then copies it into t.
Note: These are not assignments; a default What about
copy constructor is called. Time t = 3;
?
In mixed-mode operations, compiler will look for a 32
constructor to convert one value to the type of the other.
This is OK
There is a default copy operation for assignment. for now
Example:
t = mealTime;
12 12
0 0
A A
0 0
Testing:
Time mealTime;
. . .
cout << "Hour: " << mealTime.Hour() << endl;
Execution:
Hour: 12
35
Output and Input
Add output operation(s) to a class early so it can be used for
debugging other operations.
Example: overload operator<<() for a Time object
Instead of:
cout << "We'll be eating at " ;
mealTime.Display(cout);
cout << endl;
we can write:
cout << "We'll be eating at "
<< mealTime << endl;
36
Overloading operators
In C++, operator can be implemented with the function
operator().
37
Overloading Output Operator <<
Can operator<<() be a member function?
No, because the compiler will treat
cout << t
as
cout.operator<<(t)
which means that operator<<(const Time &) would have to be a
member of class ostream (or cout be of type Time)
and we can't (or don't want to) modify standard C++ classes.
Putting the prototype
ostream& operator<<(ostream & out, const Time& t);
inside our class declaration produces a compiler error like:
'Time::operator <<(ostream &, const Time &)'
must take exactly one argument
38
Why is out a reference parameter?
So corresponding actual ostream argument gets modified
when out does.
Why is t a const reference parameter?
Avoid overhead of copying a class object.
Why is return type ostream & (a reference to an ostream)?
Else a copy of out is returned.
Why return out?
So we can chain output operators.
Since << is left - associative:
( ( ( cout << t1)<< endl) << t2)<< endl;
operator<<(cout, t1) << endl << t2 << endl;
first function must return cout
cout << endl << t2 << endl;
operator<<(cout, endl) << t2 << endl;
cout << t2 << endl;
. . . 39
Overloading << for a Class
Method 1: Put definition in .h file, after class declaration, and
have it call an output function member.
Inline it, because it's simple.
. . .
} // end of class declaration
. . .
/* operator<< displays time in standard and military
format.
Receive: ostream out and Time object t
Output: time represented by Time object t
Pass back: ostream out with t inserted into it
Return: out
*/
inline ostream & operator<<(ostream & out, const Time & t)
{
t.Display(out);
return out; Note: No Time:: 40
} It's not a member function!
Method 2: Define operator<<()outside the class declaration
as a non-member function and:
(i) use accessors to display data members, or
(ii) declare it to be a friend in the class declaration.
In class declaration
/* doc. as before */
friend ostream & operator<<(ostream & out, const Time & t);
43
Relational Operators (<)
Specification:
Receives: Two Time objects
Returns: True if the first object is less than the second; false otherwise.
However . . .
45
Caveat re Operator Overloading
Internal perspective may lead to seeming inconsistencies:
Example: Suppose a and b are objects of type class C.
a < b okay?
Yes, equivalent to a.operator<(b)
b < a okay?
Yes, equivalent to b.operator<(a)
a < 2 okay?
Yes, if there is a constructor that promotes 2 to type C,
since this is then equivalent to a.operator<(C(2))
2 < a okay?
No, equivalent to 2.operator<(a), which is meaningless.
May confuse an application programmer to support a < 2 but disallow
2 < a. probably best to use friends here. 46
Overloaded Operator as Friend
External perspective: (Permits a < 2 and 2 < a , if 2 can be promoted)
class Time
{
public: // member functions
. . .
/* operator<
* Receive: Two Times t1 and t2
* Return: True if time t1 is less than time t2/
*/
friend bool operator<(const Time & t1, const Time & t2);
. . .
}; // end of class declaration
inline bool Time::operator<
(const Time & t1, const Time & t2)
{ return t1.myMilTime < t2.myMilTime; }
Or don't use friend and compare values obtained by accessors. 47
Adding Increment/Decrement Operators
Specification:
Receives:A Time object (perhaps implicitly)
Returns: The Time object with minutes incremented by 1
minute. Ye
Question: Should it be a member function?
s
Add to Time.h:
void Advance();
48
Add to Time.cpp:
//----- Function to implement Advance() -----
void Time::Advance()
{
myMinutes++;
myHours += myMinutes / 60;
myMinutes %= 60; myHours %= 12;
if (myMilTime == 1159)
myAMorPM = 'P';
else if (myMilTime == 2359)
myAMorPM = 'A';
// else no change
myMilTime =
ToMilitary(myHours, myMinutes, myAMorPM);
} 49
++
e.g.,
Program needs Time class, so it #includes "Time.h"
Program also needs library Lib, so it #includes "Lib.h"
. . . but Lib.h also includes "Time.h”
51
Conditional Compilation
Wrap the declarations in Time.h inside preprocessor directives.
(Preprocessor scans through file removing comments, #including files, and
processing other directives (which begin with #) before the file is passed to the
compiler.)