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