CSE 230 - C++ Notes 3
Classes and Data Abstraction – Part II
Constant Objects
•The principle of “least privilege” can be applied to objects that are not modifiable.
•The keyword ______ may be used to indicate that an object will not be modified after it is initialized.
•Example:
const Time noon(12, 0, 0);
•C++ disallow any member function calls for ______ objects unless the functions themselves are declared const. This includes the get functions as well.
•A function is specified both in its prototype and in its definition by inserting const after the parameter list.
•Example:
int Time::getHour() const {return hour;}
Constant Objects(cont’d)
•An interesting problem arises for constructors and destructors, each of which often needs to modify objects.
•A constructor must be allowed to modify an object so that the object can be initialized properly. It can also call ______functions to initialize a ______ object.
•A destructor must be able to perform its termination housekeeping chores before an object is destroyed.
•The ______declaration is not allowed for constructors and destructors.
#ifndef TIME5_H
#define TIME5_H
class Time {
public:
Time( int = 0, int = 0, int = 0 ); // default constructor –can’t be declared as a const
void setTime( int, int, int ); /* set functions */
void setHour( int );
void setMinute( int );
void setSecond( int );
int getHour() const; /* get functions (normally declared const)*/
int getMinute() const;
int getSecond() const;
void printMilitary() const; /* prints functions(normally declared const) */
void printStandard(); // should be const
private:
int hour;
int minute;
int second;
}; // end class Time (interface)
#endif
#include <iostream>
using std::cout;
#include "time5.h"
Time::Time( int hr, int min, int sec ) { setTime( hr, min, sec ); }
void Time::setTime(int h, int m, int s ) // non-const
{
setHour( h );
setMinute( m );
setSecond( s );
} // end function setTime
void Time::setHour( int h ) { hour = ( h >= 0 & h < 24 ) ? h : 0; }
void Time::setMinute( int m ){ minute = (m>=0 & m<60 ) ? m : 0;}
void Time::setSecond( int s ) { second = ( s >= 0 & s < 60 ) ? s : 0;}
int Time::getHour() ______{ return hour; }
int Time::getMinute() ______{ return minute; }
int Time::getSecond() ______{ return second; }
// Display military format time: HH:MM
void Time::printMilitary() const
{
cout < ( hour < 10 ? "0" : "" ) < hour < ":"
< ( minute < 10 ? "0" : "" ) < minute;
} // end function printMilitary
// Display standard format time: HH:MM:SS AM (or PM)
void Time::printStandard() // should be const
{
cout < ( ( hour == 12 ) ? 12 : hour % 12 ) < ":"
< ( minute < 10 ? "0" : "" ) < minute < ":"
< ( second < 10 ? "0" : "" ) < second
< ( hour < 12 ? " AM" : " PM" );
} // end function printStandard
// end class Time (implementation)
#include "time5.h"
int main()
{
Time wakeUp( 6, 45, 0 ); //non-const object
const Time noon( 12, 0, 0 );//const object calls non-const constructor
// MEMBER FUNCTION OBJECT
wakeUp.setHour( 18 ); // non-const ______
noon.setHour( 12 ); // non-const const*
wakeUp.getHour(); // const ______
noon.getMinute(); // const const
noon.printMilitary(); // const const
noon.printStandard(); // non-const const*
return 0;
} // end function main
* Depending on the compiler, an error or warning message is issued.
Constant Data Members
•A member______ is used to initialize a private data member.
•The format is as follows:
className::constructorName (parameter list)
: privateDataName( value )
{ other statements }
•For example:
Increment::Increment(int c, int i)
: increment( i )
{ count = c;}
•All data members (including non-const) can be initialized using member ______. For multiple initializations, include them in a comma-separated list after the colon.
•For example:
Increment::Increment(int c, int i) : increment( ____ ), count(_____ ) { }
#include <iostream>
using std::cout;
using std::endl;
class Increment {
public:
Increment( int c = 0, int i = 1 );
void addIncrement() { count += increment; }
void print() const;
private:
int count;
constint increment; // const data member – not necessary to initialize
}; // end class Increment
Increment::Increment( int c, int i ) : increment( i ) // initializer for const member
{ count = c; } // increment = i; is a syntax error
void Increment::print() ______
{
cout < "count = " < count
< ", increment = " < increment < endl;
} // end function print
int main()
{
Increment value( 10, 5 );
cout < "Before incrementing: ";
value.print();
for ( int j = 0; j < 3; j++ ) {
value.addIncrement();
cout < "After increment " < j + 1 < ": ";
value.print();
} // end for
return 0;
} // end function main
Before incrementing: count = ______, increment = 5
After increment 1: count = ______, increment = 5
After increment 2: count = ______, increment = 5
After increment 3: count = ______, increment = 5
Composition of Objects
•A class can have objects of other classes as members.
•Whenever an object is created, its constructor is called, so we need to specify how arguments are passed to member-objects constructors.
•Member objects are constructed in the order in which they are declared (not in the order they are listed in the constructor’s initializer list).
•Objects are constructed from the inside out and destructed in the reverse order.
#ifndef DATE_H
#define DATE_H
class Date {
public:
Date( int = 1, int = 1, int = 1900 ); // default constructor
void print() const; // print date in month/day/year format
~Date(); // confirm destruction order
private:
int month; // 1-12
int day; // 1-31 based on month
int year; // any year
int checkDay( int ); // utility function to test proper day for month and year
}; // end class Date (interface)
#endif
#include <iostream>
using std::cout;
using std::endl;
#include "date.h"
Date::Date( int mn, int dy, int yr )
{
if ( mn > 0 & mn <= 12 ) // validate the month
month = mn;
else {
month = 1;
cout < "Month " < mn < " invalid. Set to month 1.\n";
} // end else
year = yr; // should validate yr
day = checkDay( dy ); // validate the day
cout < "Date object constructor for date ";
print(); // print with no arguments
cout < endl;
} // end Date constructor
void Date::print() const { cout < month < '/' < day < '/' < year; }
Date::~Date() // Destructor: provided to confirm destruction order
{
cout < "Date object destructor for date ";
print(); cout < endl;
} // end Date destructor
int Date::checkDay( int testDay ) // test for proper day value
{
staticconstint daysPerMonth[ ____ ] =
{____, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
if ( testDay > _____ & testDay <= ______[ ______] )
return testDay;
if ( month == _____ & testDay == ______& // February: Check for leap year
( year % 400 == 0 ||
( year % 4 == 0 & year % 100 != 0 ) ) )
return testDay;
cout < "Day " < testDay < " invalid. Set to day 1.\n";
return 1; // leave object in consistent state if bad value
} // end function checkDay
// end class Date (implementation)
#ifndef EMPLOYEE _H
#define EMPLOYEE _H
#include "date.h"
class Employee {
public:
Employee( char *, char *, int, int, int, int, int, int );
void print() const;
~Employee(); // provided to confirm destruction order
private:
char firstName[ 25 ]; // fixed size up to 25 characters
char lastName[ 25 ];
const Date birthDate;
const Date hireDate;
}; // end class Employee (interface)
#include <iostream>
using std::cout;
using std::endl;
#include <cstring>
#include "employee.h"
#include "date.h"
Employee::Employee( char *fname, char *lname, int bmonth, int bday, int byear,
int hmonth, int hday, int hyear )
: birthDate( bmonth, bday, byear ), hireDate( hmonth, hday, hyear )
{
int length = strlen( fname ); // copy fname
length = ( length < 25 ? length : 24 );// into firstName
strncpy( firstName, fname, length ); // and be sure
firstName[ length ] = '\0'; // that it fits
length = strlen( lname ); // copy lname
length = ( length < 25 ? length : 24 );// into lastName
strncpy( lastName, lname, length ); // and be sure
lastName[ length ] = '\0'; // that it fits
cout < "Employee object constructor: " < firstName < ' ' < lastName < endl;
} // end Employee constructor
void Employee::print() const
{
cout < lastName < ", " < firstName < "\nHired: ";
hireDate.print();
cout < " Birth date: ";
birthDate.print();
cout < endl;
} // end function print
// Destructor: provided to confirm destruction order
Employee::~Employee()
{
cout < "Employee object destructor: "
< lastName < ", " < firstName < endl;
} // end Employee destructor
// end class Employee (implementation)
#include <iostream>
using std::cout;
using std::endl;
#include " employee.h"
int main()
{
Employee e( "Bob", "Jones", 7, 24, 1949, 3, 12, 1988 );
cout < '\n';
e.print();
cout < "\nTest Date constructor with invalid values:\n";
Date d( 14, 35, 1994 ); // invalid Date values
cout < endl; // this can be done by creating another function
return 0;
} // end function main
Order of constructions and destructions:
Date object constructor for date ___/___/____
Date object constructor for date ___/___/____
Employee object constructor for : Bob Jones
Date object constructor for invalid date. Set to 1/1/1994
Date object destructor for date /1/1994
Employee object destructor for : Bob Jones
Date object destructor for date ____/___/_____
Date object destructor for date ____/___/_____
Friend Functions and Classes
•A friend function of a class is defined outside that class’s scope, yet has the right to access ______members of the class.
•A function or an entire class may be declared to be a friend of another class.
•Using friend functions can enhance performance and it is often appropriate when a member function can not be used for certain operations.
•To declare a friend function, precede the function prototype with the keyword friend.
•To declare classTwo as a friendof classOne, place a declaration of the following form in the definition of classOne:
friendclass classTwo;
•Friendship is granted (not taken) and is neither symmetric nor transitive.
#include <iostream>
using std::cout;
using std::endl;
class Count {
friend void setData( Count &, int ); // friend declaration
public:
Count() { data = 0; } // constructor
void print() const { cout < data < endl; }
private:
int data;
}; // end class Count
void setData( Count &c, int val ) {
c.data = val; // legal: setData is a friend of Count
} // end function setX
int main()
{
Count counter;
cout < "counter.data after instantiation: ";
counter.print();
cout<"counter.data after call to setData friend function:";
setData( counter, 8 ); // set data with a friend
counter.print();
return 0;
} // end function main
Using this
•Every object has access to its own address through a pointer called this.
•An object’s this pointer is ______ part of the object (has no effect in the ______). Rather, this is passed into the object (by the compiler) as an implicit first argument on every non-______ member function.
•The this pointer is implicitly used to reference both the data members and member functions of an object. It can also be used explicitly.
•The type of the this pointer depends on type of object.
•Example:
Employee * const (const pointer to an object)
constEmployee * const (const pointer to an object that is constant)
•
#include <iostream>
using std::cout;
using std::endl;
class Test {
public:
Test( int = 0 ); // default constructor
void print() const;
private:
int data;
}; // end class Test
Test::Test( int a ) { data = a; } // constructor
void Test::print() const { // ( ) around *this required
cout < data < this->data < (*this ).data < endl;
} // end function print
int main()
{
Test testObject( 12 );
testObject.print();
return 0;
} // end function main
Dynamic Memory Allocation
•In C:
TypeName *typeNamePtr;
typeNamePtr = malloc( sizeof(TypeName) );
•In C++ use _____ typeName to create a new space:
double *somePtr = ______double(3.14);
int *arrayPtr = ______int[10];
char *str = ______char[20];
•Use ______typeName to destroy an allocated space:
______somePtr;
______[ ] arrayPtr; // [ ] must be used for arrays
•new and delete automatically call the class constructor and destructor respectively.
Static Class Members
•Each object of a class has its own copy of all the data members of the class.
•A static class variable is shared by all objects of a class and it represents “class-wide” information (i.e. a property of the class, not of a specific object).
•A static data member must be initialized once at file scope.
•Although static data members may seem like global variables, but they have class scope.
•A static member function has no ______pointer and referring to it is a syntax error. Also, it can’t be a ______.
•The member function may be declared static if it does not access non-static class data members and member functions.
#ifndef EMPLOYEE_H
#define EMPLOYEE_H
class Employee {
public:
Employee( const char*, const char* ); // constructor
~Employee(); // destructor
constchar *getFirstName() const; // return first name
constchar *getLastName() const; // return last name
// static member function
staticint getCount(); // return # objects instantiated
private:
char *firstName;
char *lastName;
// static data member
staticint count; // number of objects instantiated
}; // end class Employee (interface)
#endif
#include <iostream>
using std::cout;
using std::endl;
#include <cstring>
#include <cassert>
#include "employee.h"
int Employee::count = 0; // Initialize the static data member
int Employee::getCount() { return count; } // static member function
Employee::Employee( constchar *first, constchar *last )
{
firstName = ______char[ strlen( first ) + 1 ];
assert( firstName != 0 ); // ensure memory allocated
strcpy( firstName, first );
lastName = ______char[ strlen( last ) + 1 ];
assert( lastName != 0 ); // ensure memory allocated
strcpy( lastName, last );
++count; // increment static count of employees
cout<"Employee constructor for " < firstName< ' ' < lastName < " called.\n“
} // end Employee constructor
// Destructor deallocates dynamically allocated memory
Employee::~Employee()
{
cout < "~Employee() called for " < firstName < ' ' < lastName < endl;
______[ ] firstName; // recapture memory
______[ ] lastName; // recapture memory
--count; // decrement static count of employees
} // end Employee destructor
constchar *Employee::getFirstName() const
{
// Const before return type prevents client from modifying
// private data. Client should copy returned string before
// destructor deletes storage to prevent undefined pointer.
return firstName;
} // end function getFirstName
constchar *Employee::getLastName() const
{
return lastName;
} // end function getLastName
// end class Employee (implementation)
#include <iostream>
using std::cout;
using std::endl;
#include "employee.h"
int main()
{ // class name
cout < “# of employees before instantiation is"< Employee::getCount() < endl;
Employee *e1Ptr = new Employee( "Susan", "Baker" );
Employee *e2Ptr = ______Employee( "Robert", "Jones" );
cout < “#of employees after instantiation is" < e1Ptr->getCount();
cout <"\nEmployee 1:"<e1Ptr->getFirstName()<" "< e1Ptr->getLastName()
<"\nEmployee 2:" <e2Ptr->getFirstName()<" "<e2Ptr->getLastName();
<“\n\n”;
______e1Ptr; // recapture memory
e1Ptr = 0; // disconnect e1Ptr from the previously allocated space
______e2Ptr; // recapture memory
e2Ptr = 0; // class name
cout<"Number of employees after deletion is"< Employee::getCount() < endl;
return 0;
} // end function main
1