When we deal with classes and objects, we are suddenly forced to deal with functions that we take for granted with simple types like int and double.
Every class should have at least one constructor. The simplest one takes no arguments and contains no actual code. For example, a class called Thing would have the constructor:
Thing( ) { ; }
The compiler will automatically create this function for you
if you don't. The advantage of you actually typing it in is that
you can add code to it later.
Every class should also have a copy constructor and an assignment operator. These are two functions which ensure that an object's member data gets copied correctly. Like a default constructor, the compiler will automatically write a copy constructor and assignment operator for each class, if you don't.
Most of the time, those automatically generated functions will work correctly. However, if you are dealing with any dynamically-allocated data, then you *must* write your own copy constructor and assignment operator.
A copy constructor, as the name implies, is a constructor, giving you a new object that is an "identical copy" of another object. (What is meant by "an identical copy" is up to the designer of the class -- not all data members may need to be copied.)
The general form of a copy constructor takes a constant (i.e., read-only) reference to an object of the same class. For the Thing class mentioned above, the copy constructor declaration would look like:
Thing( const Thing & );
An assignment operator is a function that gets called when you write an assignment statement, such as a = b;. Typically, you will assign one object to another object from the same class. The general format takes a constant reference to an object and returns a reference to an object. For example:
Thing & operator=( const Thing & );The parameter is the object on the right of the equal sign, and the return value is the object on the left. The assignment statement a = b translates into:
a.operator=( b );This return value also allows you to chain assignment statements together, e.g., a = b = c, which translates into:
a.operator=( b.operator=( c ) );
The difference between a copy constructor and assignment operator is that a copy constructor gives you a brand new object, while an assignment operator modifies an existing object.
#if !defined(__HOCKEY_TEAM_H__)
#define __HOCKEY_TEAM_H__
class ostream; // "Forward class declaration": so we don't
// have to #include <iostream.h>
//
// Class to model a hockey team
//
class HockeyTeam
{
private:
char * itsConference; // Conference
char * itsDivision; // Division
char * itsName; // Name of team (city)
int itsWins; // Number of games won
int itsLosses; // Number of games lost
int itsTies; // Number of games tied
int itsGoalsFor; // Total goals scored by this team
int itsGoalsAgainst; // Total goals scored against this team
int itsPoints; // Total number of points
public:
HockeyTeam( const char * theName = 0 ); // "Constructor"
HockeyTeam( const HockeyTeam & ); // "Copy Constructor"
~HockeyTeam( ); // "Destructor"
// "Assignment Operator"
HockeyTeam & operator=( const HockeyTeam & );
// "Accessors"
const char * getConference( ) const { return itsConference; }
const char * getDivision( ) const { return itsDivision; }
const char * getName( ) const { return itsName; }
int getWins( ) const { return itsWins; }
int getLosses( ) const { return itsLosses; }
int getTies( ) const { return itsTies; }
int getGoalsFor( ) const { return itsGoalsFor; }
int getGoalsAgainst( ) const { return itsGoalsAgainst; }
int getPoints( ) const { return itsPoints; }
int gamesPlayed( ) const
{ return itsWins + itsTies + itsLosses; }
// "Mutators"
void setConference( const char * theConference );
void setDivision( const char * theDivision );
void changeName( const char * theName );
void reorganize( const char * theConference,
const char * theDivision );
void applyGame( int goalsFor, int goalsAgainst );
void applyGame( int goalsFor, int goalsAgainst, bool bOT );
};
//////////////////////////////////////////////////////////////////////////
// Function: operator<<
// Description: team insertion operator
// Parameters: ostr - an output stream, theTeam - a team
// Returns: ostr
//////////////////////////////////////////////////////////////////////////
inline ostream &
operator<<( ostream & ostr, const HockeyTeam & theTeam )
{
ostr << theTeam.getConference( ) << ' ';
ostr << theTeam.getDivision( ) << ' ';
ostr << theTeam.getName( ) << ' ';
ostr << theTeam.getWins( ) << ' ';
ostr << theTeam.getLosses( ) << ' ';
ostr << theTeam.getTies( ) << ' ';
ostr << theTeam.getGoalsFor( ) << ' ';
ostr << theTeam.getGoalsAgainst( ) << ' ';
ostr << theTeam.getPoints( );
return ostr;
}
#endif /* __HOCKEY_TEAM_H__ */
Download this file
#include <iostream.h>
#include <string.h>
#include "HockeyTeam3A.h"
HockeyTeam::HockeyTeam( const char * theName ) :
// "Memberwise initialization"
itsConference( 0 ),
itsDivision( 0 ),
itsName( 0 ),
itsWins( 0 ),
itsLosses( 0 ),
itsTies( 0 ),
itsGoalsFor( 0 ),
itsGoalsAgainst( 0 ),
itsPoints( 0 )
{
# if defined(DEBUG)
cout << "HockeyTeam constructor called" << endl;
# endif
if ( theName != 0 )
{
itsName = new char [ strlen( theName ) + 1 ];
strcpy( itsName, theName );
}
}
// Copy constructor
HockeyTeam::HockeyTeam( const HockeyTeam & theTeam ) :
// "Memberwise initialization"
itsConference( 0 ),
itsDivision( 0 ),
itsName( 0 ),
itsWins( theTeam.itsWins ),
itsLosses( theTeam.itsLosses ),
itsTies( theTeam.itsTies ),
itsGoalsFor( theTeam.itsGoalsFor ),
itsGoalsAgainst( theTeam.itsGoalsAgainst ),
itsPoints( theTeam.itsPoints )
{
# if defined(DEBUG)
cout << "HockeyTeam copy constructor called" << endl;
# endif
if ( theTeam.itsConference != 0 )
{
itsConference = new char [ strlen( theTeam.itsConference ) + 1 ];
strcpy( itsConference, theTeam.itsConference );
}
if ( theTeam.itsDivision != 0 )
{
itsDivision = new char [ strlen( theTeam.itsDivision ) + 1 ];
strcpy( itsDivision, theTeam.itsDivision );
}
if ( theTeam.itsName != 0 )
{
itsName = new char [ strlen( theTeam.itsName ) + 1 ];
strcpy( itsName, theTeam.itsName );
}
}
HockeyTeam::~HockeyTeam( )
{
# if defined(DEBUG)
cout << "HockeyTeam destructor called" << endl;
# endif
delete [] itsConference;
delete [] itsDivision;
delete [] itsName;
}
// Assignment operator
HockeyTeam &
HockeyTeam::operator=( const HockeyTeam & theTeam )
{
# if defined(DEBUG)
cout << "HockeyTeam assignment operator called" << endl;
# endif
// prevent self-assignment
if ( this != &theTeam )
{
delete [] itsConference;
itsConference = 0;
if ( theTeam.itsConference != 0 )
{
itsConference = new char [
strlen( theTeam.itsConference ) + 1 ];
strcpy( itsConference, theTeam.itsConference );
}
delete [] itsDivision;
itsDivision = 0;
if ( theTeam.itsDivision != 0 )
{
itsDivision = new char [
strlen( theTeam.itsDivision ) + 1 ];
strcpy( itsDivision, theTeam.itsDivision );
}
delete [] itsName;
itsName = 0;
if ( theTeam.itsName != 0 )
{
itsName = new char [ strlen( theTeam.itsName ) + 1 ];
strcpy( itsName, theTeam.itsName );
}
itsWins = theTeam.itsWins;
itsLosses = theTeam.itsLosses;
itsTies = theTeam.itsTies;
itsGoalsFor = theTeam.itsGoalsFor;
itsGoalsAgainst = theTeam.itsGoalsAgainst;
itsPoints = theTeam.itsPoints;
}
return *this;
}
void
HockeyTeam::setConference( const char * theConference )
{
if ( theConference != 0 )
{
delete [] itsConference;
itsConference = new char [ strlen( theConference ) + 1 ];
strcpy( itsConference, theConference );
}
}
void
HockeyTeam::setDivision( const char * theDivision )
{
if ( theDivision != 0 )
{
delete [] itsDivision;
itsDivision = new char [ strlen( theDivision ) + 1 ];
strcpy( itsDivision, theDivision );
}
}
void
HockeyTeam::changeName( const char * theName )
{
if ( theName != 0 )
{
delete [] itsName;
itsName = new char [ strlen( theName ) + 1 ];
strcpy( itsName, theName );
}
}
void
HockeyTeam::reorganize( const char * theConference, const char * theDivision )
{
if ( ( theConference != 0 ) && ( theDivision != 0 ) )
{
setConference( theConference );
setDivision( theDivision );
}
}
void
HockeyTeam::applyGame( int goalsFor, int goalsAgainst )
{
applyGame( goalsFor, goalsAgainst, false );
}
void
HockeyTeam::applyGame( int goalsFor, int goalsAgainst, bool bOvertime )
{
itsGoalsFor += goalsFor;
itsGoalsAgainst += goalsAgainst;
if ( goalsFor > goalsAgainst )
{
itsPoints += 2;
itsWins += 1;
}
else if ( goalsFor < goalsAgainst )
{
itsLosses += 1;
if ( bOvertime )
{
itsPoints += 1;
}
}
else // must be == (a tie)
{
itsTies += 1;
itsPoints += 1;
}
}
Download this file
#if !defined(__HOCKEY_TEAM_H__)
#define __HOCKEY_TEAM_H__
#include <string>
using namespace std;
//
// Class to model a hockey team
//
class HockeyTeam
{
private:
string itsConference; // Conference
string itsDivision; // Division
string itsName; // Name of team (city)
int itsWins; // Number of games won
int itsLosses; // Number of games lost
int itsTies; // Number of games tied
int itsGoalsFor; // Total goals scored by this team
int itsGoalsAgainst; // Total goals scored against this team
int itsPoints; // Total number of points
public:
HockeyTeam( const char * theName = 0 ); // "Default Constructor"
HockeyTeam( const HockeyTeam & ); // "Copy Constructor"
~HockeyTeam( ); // "Destructor"
// "Assignment Operator"
HockeyTeam & operator=( const HockeyTeam & );
// "Accessors"
const char * getConference( ) const
{ return itsConference.data( ); }
const char * getDivision( ) const
{ return itsDivision.data( ); }
const char * getName( ) const
{ return itsName.data( ); }
int getWins( ) const { return itsWins; }
int getLosses( ) const { return itsLosses; }
int getTies( ) const { return itsTies; }
int getGoalsFor( ) const { return itsGoalsFor; }
int getGoalsAgainst( ) const { return itsGoalsAgainst; }
int getPoints( ) const { return itsPoints; }
int gamesPlayed( ) const
{ return itsWins + itsTies + itsLosses; }
// "Mutators"
void setConference( const char * theConference );
void setDivision( const char * theDivision );
void changeName( const char * theName );
void reorganize( const char * theConference,
const char * theDivision );
void applyGame( int goalsFor, int goalsAgainst );
void applyGame( int goalsFor, int goalsAgainst, bool bOT );
};
//////////////////////////////////////////////////////////////////////////
// Function: operator<<
// Description: team insertion operator
// Parameters: ostr - an output stream, theTeam - a team
// Returns: ostr
//////////////////////////////////////////////////////////////////////////
inline ostream &
operator<<( ostream & ostr, const HockeyTeam & theTeam )
{
ostr << theTeam.getConference( ) << ' ';
ostr << theTeam.getDivision( ) << ' ';
ostr << theTeam.getName( ) << ' ';
ostr << theTeam.getWins( ) << ' ';
ostr << theTeam.getLosses( ) << ' ';
ostr << theTeam.getTies( ) << ' ';
ostr << theTeam.getGoalsFor( ) << ' ';
ostr << theTeam.getGoalsAgainst( ) << ' ';
ostr << theTeam.getPoints( );
return ostr;
}
#endif /* __HOCKEY_TEAM_H__ */
Download this file
#include "HockeyTeam3B.h"
#include <iostream>
#include <string>
HockeyTeam::HockeyTeam( const char * theName ) :
// "Memberwise initialization"
itsConference( ),
itsDivision( ),
itsName( ),
itsWins( 0 ),
itsLosses( 0 ),
itsTies( 0 ),
itsGoalsFor( 0 ),
itsGoalsAgainst( 0 ),
itsPoints( 0 )
{
changeName( theName );
# if defined(DEBUG)
cout << "HockeyTeam constructor called (" << itsName << ")" << endl;
cout << "this = " << reinterpret_cast<unsigned long>( this ) << endl;
# endif
}
HockeyTeam::HockeyTeam( const HockeyTeam & theTeam ) :
// "Memberwise initialization"
itsConference( theTeam.itsConference ),
itsDivision( theTeam.itsDivision ),
itsName( theTeam.itsName ),
itsWins( theTeam.itsWins ),
itsLosses( theTeam.itsLosses ),
itsTies( theTeam.itsTies ),
itsGoalsFor( theTeam.itsGoalsFor ),
itsGoalsAgainst( theTeam.itsGoalsAgainst ),
itsPoints( theTeam.itsPoints )
{
# if defined(DEBUG)
cout << "HockeyTeam copy constructor called" << endl;
cout << "this = " << reinterpret_cast<unsigned long>( this ) << endl;
cout << "theTeam = " << reinterpret_cast<unsigned long>( &theTeam )
<< endl;
# endif
;
}
HockeyTeam::~HockeyTeam( )
{
# if defined(DEBUG)
cout << "HockeyTeam destructor called (" << itsName << ")" << endl;
cout << "this = " << reinterpret_cast<unsigned long>( this ) << endl;
# endif
}
HockeyTeam &
HockeyTeam::operator=( const HockeyTeam & theTeam )
{
# if defined(DEBUG)
cout << "HockeyTeam assignment operator called" << endl;
cout << "this = " << reinterpret_cast<unsigned long>( this ) << endl;
cout << "theTeam = " << reinterpret_cast<unsigned long>( &theTeam )
<< endl;
# endif
// prevent self-assignment
if ( this != &theTeam )
{
itsConference = theTeam.itsConference;
itsDivision = theTeam.itsDivision;
itsName = theTeam.itsName;
itsWins = theTeam.itsWins;
itsLosses = theTeam.itsLosses;
itsTies = theTeam.itsTies;
itsGoalsFor = theTeam.itsGoalsFor;
itsGoalsAgainst = theTeam.itsGoalsAgainst;
itsPoints = theTeam.itsPoints;
}
return *this;
}
void
HockeyTeam::setConference( const char * theConference )
{
if ( theConference != 0 )
{
itsConference = theConference;
}
}
void
HockeyTeam::setDivision( const char * theDivision )
{
if ( theDivision != 0 )
{
itsDivision = theDivision;
}
}
void
HockeyTeam::changeName( const char * theName )
{
if ( theName != 0 )
{
itsName = theName;
}
}
void
HockeyTeam::reorganize( const char * theConference, const char * theDivision )
{
if ( ( theConference != 0 ) && ( theDivision != 0 ) )
{
setConference( theConference );
setDivision( theDivision );
}
}
void
HockeyTeam::applyGame( int goalsFor, int goalsAgainst )
{
applyGame( goalsFor, goalsAgainst, false );
}
void
HockeyTeam::applyGame( int goalsFor, int goalsAgainst, bool bOvertime )
{
itsGoalsFor += goalsFor;
itsGoalsAgainst += goalsAgainst;
if ( goalsFor > goalsAgainst )
{
itsPoints += 2;
itsWins += 1;
}
else if ( goalsFor < goalsAgainst )
{
itsLosses += 1;
if ( bOvertime )
{
itsPoints += 1;
}
}
else // must be == (a tie)
{
itsTies += 1;
itsPoints += 1;
}
}
Download this file
// ...
// Copy constructor automatically called here, because we are passing
// a HockeyTeam object by value
void
display1( HockeyTeam theTeam )
{
cout << "In " << theTeam.gamesPlayed( ) << " games: " << endl;
cout << theTeam << endl << endl;
}
// Copy constructor not called here, because we are passing
// a HockeyTeam object by reference
void
display2( const HockeyTeam & theTeam )
{
cout << "In " << theTeam.gamesPlayed( ) << " games: " << endl;
cout << theTeam << endl << endl;
}
// Copy constructor not called here, because we are passing
// a HockeyTeam object by pointer (another form of by reference)
void
display3( const HockeyTeam * theTeam )
{
cout << "In " << theTeam->gamesPlayed( ) << " games: " << endl;
cout << *theTeam << endl << endl;
}
int
main( )
{
// Constructor called here with the argument "Toronto Maple Leafs"
HockeyTeam leafs( "Toronto Maple Leafs" );
// Constructor called here with the argument 0 (null pointer), which
// is automatically filled in by the compiler
HockeyTeam toronto;
leafs.reorganize( "Eastern", "Northeast" );
leafs.applyGame( 4, 3 ); // 4 - 3 regulation time win
leafs.applyGame( 4, 5, true ); // 5 - 4 overtime loss
display1( leafs );
// Assignment operator called here. Formally, it is:
// toronto.operator=( leafs );
toronto = leafs;
display2( toronto );
// Pass a pointer to the toronto object. To make a pointer, we
// take the address of the object.
display3( &toronto );
return 0;
}
Download the test program for Listings 1A and 1B
Download the test program for Listings 2A and 2B