Chapter 14 More about Classes
14.1 Instance and Static Members
Concept:
- Each object (or instance) of a class gets its own set of instance variables.
- A
staticmember variable is shared among all instances of a class. - A
staticmember function can be called without creating any instances of the class.
Instance Variables
- Every object created from a class has its own distinct copies of the class’s member variables.
- These are known as instance variables because they belong to a specific instance.
- For example, if you create two
Rectangleobjects,box1andbox2, each will have its own separatewidthandlengthvariables.
Rectangle box1, box2;
box1.setWidth(5);
box1.setLength(10);
box2.setWidth(500);
box2.setLength(1000);- Calling
box1.getWidth()returns the value frombox1, andbox2.getWidth()returns the value frombox2.
cout << box1.getWidth() << " " << box2.getWidth() << endl;Static Members
- It’s possible to create members that belong to the class itself, rather than to any specific instance.
- These are called static member variables and static member functions.
- A static variable is not stored within an instance; it’s a single, shared variable for the entire class.
- Static functions can operate only on static member variables, not on instance variables.
Static Member Variables
- A member variable declared with the
statickeyword exists as a single copy in memory, shared by all objects of the class. - This is useful for tasks like counting the number of instances created.
- The
Treeclass below uses a static variableobjectCountto track how manyTreeobjects exist.
Contents of Tree.h
- A static member variable is declared inside the class using the
statickeyword. - It must be defined outside the class to allocate memory for it. This is where it’s typically initialized.
- Uninitialized static member variables are automatically set to 0, but explicit initialization is good practice.
- In the
Treeclass, the constructor incrementsobjectCounteach time a new object is created.
🗊 Program 14-1
💻 Program Output
Even though three objects (
oak,elm,pine) are created, they all share a singleobjectCountvariable.Any object of the class can be used to access the static variable’s value.
cout << "We have " << oak.getObjectCount() << " trees\n";
cout << "We have " << elm.getObjectCount() << " trees\n";
cout << "We have " << pine.getObjectCount() << " trees\n";- A more practical example is the
Budgetclass, which uses a static membercorpBudgetto track the total budget for all company divisions.
Contents of Budget.h (Version 1)
#ifndef BUDGET_H
#define BUDGET_H
class Budget
{
private:
static double corpBudget;
double divisionBudget;
public:
Budget()
{ divisionBudget = 0; }
void addBudget(double b)
{ divisionBudget += b;
corpBudget += b; }
double getDivisionBudget() const
{ return divisionBudget; }
double getCorpBudget() const
{ return corpBudget; }
};
double Budget::corpBudget = 0;
#endif🗊 Program 14-2
#include <iostream>
#include <iomanip>
#include "Budget.h"
using namespace std;
int main()
{
int count;
const int NUM_DIVISIONS = 4;
Budget divisions[NUM_DIVISIONS];
for (count = 0; count < NUM_DIVISIONS; count++)
{
double budgetAmount;
cout << "Enter the budget request for division ";
cout << (count + 1) << ": ";
cin >> budgetAmount;
divisions[count].addBudget(budgetAmount);
}
cout << fixed << showpoint << setprecision(2);
cout << "\nHere are the division budget requests:\n";
for (count = 0; count < NUM_DIVISIONS; count++)
{
cout << "\tDivision " << (count + 1) << "\t$ ";
cout << divisions[count].getDivisionBudget() << endl;
}
cout << "\tTotal Budget Requests:\t$ ";
cout << divisions[0].getCorpBudget() << endl;
return 0;
}💻 Program Output
Static Member Functions
- A static member function is declared by placing the
statickeyword in its prototype.
static ReturnType FunctionName (ParameterTypeList);Key points about static member functions:
- They cannot access non-static (instance) member data.
- Static member variables exist for the entire lifetime of the program, even before any class objects are created.
- Static member functions can be called before any instances of the class are created. This allows them to access static member variables for setup or initialization tasks.
The
Budgetclass is modified below to include a static functionmainOfficethat adds to the corporate budget before any division objects are created.
Contents of Budget.h (Version 2)
#ifndef BUDGET_H
#define BUDGET_H
class Budget
{
private:
static double corpBudget;
double divisionBudget;
public:
Budget()
{ divisionBudget = 0; }
void addBudget(double b)
{ divisionBudget += b;
corpBudget += b; }
double getDivisionBudget() const
{ return divisionBudget; }
double getCorpBudget() const
{ return corpBudget; }
static void mainOffice(double);
};
#endifContents of Budget.cpp
🗊 Program 14-3
#include <iostream>
#include <iomanip>
#include "Budget.h"
using namespace std;
int main()
{
int count;
double mainOfficeRequest;
const int NUM_DIVISIONS = 4;
cout << "Enter the main office's budget request: ";
cin >> mainOfficeRequest;
Budget::mainOffice(mainOfficeRequest);
Budget divisions[NUM_DIVISIONS];
for (count = 0; count < NUM_DIVISIONS; count++)
{
double budgetAmount;
cout << "Enter the budget request for division ";
cout << (count + 1) << ": ";
cin >> budgetAmount;
divisions[count].addBudget(budgetAmount);
}
cout << fixed << showpoint << setprecision(2);
cout << "\nHere are the division budget requests:\n";
for (count = 0; count < NUM_DIVISIONS; count++)
{
cout << "\tDivision " << (count + 1) << "\t$ ";
cout << divisions[count].getDivisionBudget() << endl;
}
cout << "\tTotal Budget Requests:\t$ ";
cout << divisions[0].getCorpBudget() << endl;
return 0;
}💻 Program Output
- Static member functions are typically called using the class name and the scope resolution operator (
::).
Budget::mainOffice(amount);Note:
- If an object of the class exists, you can also call a static member function using the object name and the dot operator, just like a regular member function.
14.2 Friends of Classes
Concept:
- A friend is a function or an entire class that is not a member of a class but is granted access to its private members.
- Normally, private members are accessible only by member functions of the same class.
- A friend function is an exception; it’s an external function that can access the private members of a class.
- A friend can be a standalone function or a member function of another class.
- A class must explicitly declare which functions or other classes are its friends by using the
friendkeyword in its declaration.
friend ReturnType FunctionName (ParameterTypeList)- In this example, the
addBudgetfunction from theAuxiliaryOfficeclass is declared as a friend of theBudgetclass.
Contents of Budget.h (Version 3)
#ifndef BUDGET_H
#define BUDGET_H
#include "Auxil.h"
class Budget
{
private:
static double corpBudget;
double divisionBudget;
public:
Budget()
{ divisionBudget = 0; }
void addBudget(double b)
{ divisionBudget += b;
corpBudget += b; }
double getDivisionBudget() const
{ return divisionBudget; }
double getCorpBudget() const
{ return corpBudget; }
static void mainOffice(double);
friend void AuxiliaryOffice::addBudget(double, Budget &);
};
#endif- The friend declaration gives
AuxiliaryOffice::addBudgetpermission to access the private members ofBudget. - Note that the
Budgetobject to be modified is passed by reference to the friend function.
Contents of Auxil.h
Contents of Auxil.cpp
- A forward declaration (
class Budget;) is used inAuxil.h. - This tells the compiler that the
Budgetclass exists without providing its full definition. - It is necessary because the
addBudgetfunction prototype inAuxiliaryOfficeuses aBudgetreference parameter, and the compiler needs to know thatBudgetis a type.
Contents of Auxil.cpp
- Inside
AuxiliaryOffice::addBudget, the code can directly accessdiv.corpBudget, which is a private static member of theBudgetclass.
🗊 Program 14-4
#include <iostream>
#include <iomanip>
#include "Budget.h"
using namespace std;
int main()
{
int count;
double mainOfficeRequest;
const int NUM_DIVISIONS = 4;
cout << "Enter the main office's budget request: ";
cin >> mainOfficeRequest;
Budget::mainOffice(mainOfficeRequest);
Budget divisions[NUM_DIVISIONS];
AuxiliaryOffice auxOffices[4];
for (count = 0; count < NUM_DIVISIONS; count++)
{
double budgetAmount;
cout << "Enter the budget request for division ";
cout << (count + 1) << ": ";
cin >> budgetAmount;
divisions[count].addBudget(budgetAmount);
cout << "Enter the budget request for that division's\n";
cout << "auxiliary office: ";
cin >> budgetAmount;
auxOffices[count].addBudget(budgetAmount, divisions[count]);
}
cout << fixed << showpoint << setprecision(2);
cout << "\nHere are the division budget requests:\n";
for (count = 0; count < NUM_DIVISIONS; count++)
{
cout << "\tDivision " << (count + 1) << "\t\t$";
cout << divisions[count].getDivisionBudget() << endl;
cout << "\tAuxiliary office:\t$";
cout << auxOffices[count].getDivisionBudget() << endl << endl;
}
cout << "Total Budget Requests:\t$ ";
cout << divisions[0].getCorpBudget() << endl;
return 0;
}💻 Program Output
- An entire class can be made a friend of another class.
friend class AuxiliaryOffice;- Declaring an entire class as a friend gives all of its member functions access to the private members of the declaring class.
- This is often not a good practice, as it’s better to grant friendship only to the specific functions that need it.
Checkpoint
14.1 What is the difference between an instance member variable and a static member variable?
14.2 Static member variables are declared inside the class declaration. Where do you write the definition statement for a static member variable?
14.3 Does a static member variable come into existence in memory before, at the same time as, or after any instances of its class?
14.4 What limitation does a static member function have?
14.5 What action is possible with a static member function that isn’t possible with an instance member function?
14.6 If class
Xdeclares functionfas a friend, does functionfbecome a member of classX?14.7 Class
Yis a friend of classX, which means the member functions of classYhave access to the private members of classX. Does the friend key word appear in classY’s declaration or in classX’s declaration?
14.3 Memberwise Assignment
Concept:
- The assignment operator (
=) can be used to copy the data from one object to another of the same class. - By default, this operation copies the value of each member variable from the source object to the corresponding member variable in the destination object.
- Objects can be assigned to one another, just like other variables (except C-style arrays).
- This process is called memberwise assignment.
🗊 Program 14-5
#include <iostream>
#include "Rectangle.h"
using namespace std;
int main()
{
Rectangle box1(10.0, 10.0);
Rectangle box2 (20.0, 20.0);
cout << "box1's width and length: " << box1.getWidth()
<< " " << box1.getLength() << endl;
cout << "box2's width and length: " << box2.getWidth()
<< " " << box2.getLength() << endl << endl;
box2 = box1;
cout << "box1's width and length: " << box1.getWidth()
<< " " << box1.getLength() << endl;
cout << "box2's width and length: " << box2.getWidth()
<< " " << box2.getLength() << endl;
return 0;
}💻 Program Output
The statement
box2 = box1;copies thewidthandlengthfrombox1intobox2.Memberwise assignment also happens during initialization.
Assignment occurs between two existing objects.
Initialization happens when a new object is created.
Rectangle box1(100.0, 50.0);
Rectangle box2 = box1;- In the code above,
box2is created and initialized with the member values ofbox1.
14.4 Copy Constructors
Concept:
- A copy constructor is a special constructor that is automatically called when a new object is created and initialized with the data of an existing object of the same class.
- Default memberwise assignment is not always safe, especially for classes that manage dynamic memory (i.e., contain pointer members).
- Consider the
StudentTestScoresclass, which uses a pointertestScoresto a dynamically allocated array.
Contents of StudentTestScores.h (Version 1)
#ifndef STUDENTTESTSCORES_H
#define STUDENTTESTSCORES_H
#include <string>
using namespace std;
const double DEFAULT_SCORE = 0.0;
class StudentTestScores
{
private:
string studentName;
double *testScores;
int numTestScores;
void createTestScoresArray(int size)
{ numTestScores = size;
testScores = new double[size];
for (int i = 0; i < size; i++)
testScores[i] = DEFAULT_SCORE;}
public:
StudentTestScores(string name, int numScores)
{ studentName = name;
createTestScoresArray(numScores); }
~StudentTestScores()
{ delete [] testScores; }
void setTestScore(double score, int index)
{ testScores[index] = score; }
void setStudentName(string name)
{ studentName = name; }
string getStudentName() const
{ return studentName; }
int getNumTestScores() const
{ return numTestScores; }
double getTestScore(int index) const
{ return testScores[index]; }
};
#endif- The constructor for
StudentTestScoresdynamically allocates memory for the test scores.
StudentTestScores("Maria Jones Tucker", 5);- If one object is initialized from another using default memberwise assignment:
StudentTestScores student2 = student1;The pointer
testScoresis copied, but not the memory it points to.This results in a shallow copy, where both
student1.testScoresandstudent2.testScorespoint to the same block of memory.This leads to serious problems:
- Modifying the array through one object affects the other.
- When one object is destroyed, its destructor frees the memory. The other object is then left with a dangling pointer to deallocated memory, which can cause a crash.
The solution is to write a custom copy constructor to perform a deep copy.
A copy constructor is a constructor that takes a single reference parameter of the same class type.
It is called automatically during initialization (e.g.,
StudentTestScores student2 = student1;).It creates a new, independent copy of the dynamically allocated resources.
StudentTestScores(StudentTestScores &obj)
{ studentName = obj.studentName;
numTestScores = obj.numTestScores;
testScores = new double[numTestScores];
for (int i = 0; < length; i++)
testScores[i] = obj.testScores[i]; }Note:
- C++ requires that a copy constructor’s parameter must be a reference.
- With this copy constructor,
student2will have its own dynamically allocated array, containing a copy of the data fromstudent1.
Using const Parameters in Copy Constructors
- A copy constructor should not modify its argument.
- It is good practice to make the reference parameter
constto prevent accidental modification of the source object.
StudentTestScores(const StudentTestScores &obj)
{ studentName = obj.studentName;
numTestScores = obj.numTestScores;
testScores = new double[numTestScores];
for (int i = 0; i < numTestScores; i++)
testScores[i] = obj.testScores[i]; }- Here is the revised class with the copy constructor added.
Contents of StudentTestScores.h (Version 2)
#ifndef STUDENTTESTSCORES_H
#define STUDENTTESTSCORES_H
#include <string>
using namespace std;
const double DEFAULT_SCORE = 0.0;
class StudentTestScores
{
private:
string studentName;
double *testScores;
int numTestScores;
void createTestScoresArray(int size)
{ numTestScores = size;
testScores = new double[size];
for (int i = 0; i < size; i++)
testScores[i] = DEFAULT_SCORE; }
public:
StudentTestScores(string name, int numScores)
{ studentName = name;
createTestScoresArray(numScores); }
StudentTestScores(const StudentTestScores &obj)
{ studentName = obj.studentName;
numTestScores = obj.numTestScores;
testScores = new double[numTestScores];
for (int i = 0; i < numTestScores; i++)
testScores[i] = obj.testScores[i]; }
~StudentTestScores()
{ delete [] testScores; }
void setTestScore(double score, int index)
{ testScores[index] = score; }
void setStudentName(string name)
{ studentName = name; }
string getStudentName() const
{ return studentName; }
int getNumTestScores() const
{ return numTestScores; }
double getTestScore(int index) const
{ return testScores[index]; }
};
#endifCopy Constructors and Function Parameters
- When an object is passed by value to a function, the copy constructor is called to initialize the function’s parameter.
- The parameter must be a reference to prevent an infinite loop.
- If the parameter were not a reference (i.e., passed by value), the copy constructor would be called to create the parameter, which would in turn call the copy constructor to create its own parameter, and so on.
The Default Copy Constructor
- If you do not provide a copy constructor for your class, C++ automatically creates a default copy constructor.
- The default copy constructor performs a memberwise assignment (a shallow copy), which is the behavior that can cause problems with pointer members.
Checkpoint
14.8 Briefly describe what is meant by memberwise assignment.
14.9 Describe two instances when memberwise assignment occurs.
14.10 Describe a situation in which memberwise assignment should not be used.
14.11 When is a copy constructor called?
14.12 How does the compiler know that a member function is a copy constructor?
14.13 What action is performed by a class’s default copy constructor?
14.5 Operator Overloading
Concept:
- C++ allows you to redefine the behavior of standard operators when they are used with class objects.
Operator Overloading
- You can enable standard operators, like
+=, to work with your class objects. - For instance, instead of
today.add(5);, you could write the more intuitivetoday += 5;. - To achieve this, the operator must be overloaded, which means you provide a special function defining its behavior for your class.
Note:
- You have already seen operator overloading in action. For example, the
/operator performs floating-point division or integer division depending on the data types of its operands.
The this Pointer
- The
thispointer is a special, built-in pointer that exists in every class. - It is passed as a hidden argument to all non-static member functions.
- The
thispointer always points to the specific instance of the class that is calling the member function. - For example, when
student1.getStudentName()is called,thispoints to thestudent1object. Whenstudent2.getStudentName()is called,thispoints tostudent2.
Overloading the = Operator
While copy constructors handle initialization problems (like
StudentTestScores student2 = student1;), they do not apply to simple assignment between two existing objects (student2 = student1;).To change the default memberwise assignment behavior, you must overload the assignment operator (
=).This is done by creating a special member function called an operator function.
The following is an example of an
operator=member function for theStudentTestScoresclass.
const StudentTestScores operator=(const StudentTestScores &right)
{ if (this != &right)
{
delete[] testScores;
studentName = right.studentName;
numTestScores = right.numTestScores;
testScores = new double[numTestScores];
for (int i = 0; i < numTestScores; i++)
testScores[i] = right.testScores[i];
}
return *this;
}The function header can be broken down as follows:
Return type: The function returns a
const StudentTestScoresobject. This allows for chaining assignments (e.g.,a = b = c;).Function name: The name
operator=specifies that this function overloads the=operator.Parameter declaration: The function takes one parameter,
const StudentTestScores &right, which is a constant reference to the object on the right side of the=operator. It is a reference for efficiency andconstto prevent modification of the source object.
Note:
- The parameter is commonly named
rightto indicate it represents the object on the right side of the operator, but you can use any valid identifier.
- The operator notation
student2 = student1;is equivalent to the function call notationstudent2.operator=(student1);. - Because
operator=is a member function, it has access to the private members of therightobject passed to it.
Checking for Self-Assignment
In an
operator=function, thethispointer points to the object on the left of the=, and therightparameter refers to the object on the right.It is crucial to check for self-assignment (e.g.,
student1 = student1;).The check
if (this != &right)compares the memory address of the left object (this) with the address of the right object (&right).Without this check, if the class manages dynamic memory, the first step (
delete[] testScores;) would deallocate the memory that you are about to copy from, corrupting the object.The corrected version skips the assignment logic if the objects are the same.
const StudentTestScores operator=(const StudentTestScores &right)
{ if (this != &right)
{
delete[] testScores;
studentName = right.studentName;
numTestScores = right.numTestScores;
testScores = new double[numTestScores];
for (int i = 0; i < numTestScores; i++)
testScores[i] = right.testScores[i];
}
return *this;
}The = Operator’s Return Value
- The built-in
=operator allows for multiple assignments likea = b = c;because each assignment expression returns the value that was assigned. - To mimic this behavior, an overloaded
operator=function should return the object that received the assignment. - The statement
return *this;dereferences thethispointer to return the calling object itself, enabling chained assignments.
Copy Assignment
- An overloaded
=operator that copies data from one existing object to another is known as a copy assignment operator.
🗊 Program 14-6
#include <iostream>
#include "StudentTestScores.h"
using namespace std;
void displayStudent(StudentTestScores);
int main()
{
StudentTestScores student1("Kelly Thorton", 3);
student1.setTestScore(100.0, 0);
student1.setTestScore(95.0, 1);
student1.setTestScore(80, 2);
StudentTestScores student2("Jimmy Griffin", 5);
student2 = student1;
displayStudent(student1);
displayStudent(student2);
return 0;
}
void displayStudent(StudentTestScores s)
{
cout << "Name: " << s.getStudentName() << endl;
cout << "Test Scores: ";
for (int i = 0; i < s.getNumTestScores(); i++)
cout << s.getTestScore(i) << " ";
cout << endl;
}💻 Program Output
🗊 Program 14-7
#include <iostream>
#include "StudentTestScores.h"
using namespace std;
void displayStudent(StudentTestScores);
int main()
{
StudentTestScores student1("Kelly Thorton", 3);
student1.setTestScore(100.0, 0);
student1.setTestScore(95.0, 1);
student1.setTestScore(80, 2);
StudentTestScores student2("Jimmy Griffin", 5);
StudentTestScores student3("Kristen Lee", 10);
student3 = student2 = student1;
displayStudent(student1);
displayStudent(student2);
displayStudent(student3);
return 0;
}
void displayStudent(StudentTestScores s)
{
cout << "Name: " << s.getStudentName() << endl;
cout << "Test Scores: ";
for (int i = 0; i < s.getNumTestScores(); i++)
cout << s.getTestScore(i) << " ";
cout << endl;
}💻 Program Output
Some General Issues of Operator Overloading
- While you can redefine an operator’s behavior, it is poor practice to change its fundamental meaning (e.g., making
=display data instead of assigning it). - You cannot change the number of operands an operator takes. For example,
=must remain a binary operator. - Most C++ operators can be overloaded.
+ |
- |
* |
/ |
% |
^ |
& |
| |
~ |
! |
= |
< |
> |
+= |
-= |
*= |
/= |
%= |
^= |
&= |
|= |
<< |
>> |
>>= |
<<= |
== |
!= |
<= |
>= |
&& |
|| |
++ |
-- |
-<* |
, |
-< |
[] |
() |
new |
delete |
- The only operators that cannot be overloaded are:
?: . .* :: sizeofOverloading Math Operators
- Classes can also benefit from overloaded math operators like
+and-. - The
FeetInchesclass demonstrates this by allowing twoFeetInchesobjects to be added or subtracted.
Contents of FeetInches.h (Version 1)
#ifndef FEETINCHES_H
#define FEETINCHES_H
class FeetInches
{
private:
int feet;
int inches;
void simplify();
public:
FeetInches(int f = 0, int i = 0)
{ feet = f;
inches = i;
simplify(); }
void setFeet(int f)
{ feet = f; }
void setInches(int i)
{ inches = i;
simplify(); }
int getFeet() const
{ return feet; }
int getInches() const
{ return inches; }
FeetInches operator + (const FeetInches &);
FeetInches operator - (const FeetInches &);
};
#endifContents of FeetInches.cpp (Version 1)
#include <cstdlib>
#include "FeetInches.h"
void FeetInches::simplify()
{
if (inches >= 12)
{
feet += (inches / 12);
inches = inches % 12;
}
else if (inches < 0)
{
feet -= ((abs(inches) / 12) + 1);
inches = 12 - (abs(inches) % 12);
}
}
FeetInches FeetInches::operator + (const FeetInches &right)
{
FeetInches temp;
temp.inches = inches + right.inches;
temp.feet = feet + right.feet;
temp.simplify();
return temp;
}
FeetInches FeetInches::operator - (const FeetInches &right)
{
FeetInches temp;
temp.inches = inches - right.inches;
temp.feet = feet - right.feet;
temp.simplify();
return temp;
}- The
simplifyfunction normalizes the values, for example, converting 14 inches into 1 foot and 2 inches. - The
operator+function forlength3 = length1 + length2;works as follows:- A temporary
FeetInchesobject,temp, is created to hold the result. - The
inchesfrom the calling object (length1) and therightobject (length2) are added and stored intemp. - The
feetfrom both objects are added and stored intemp. temp.simplify()is called to normalize the result.- The
tempobject is returned and assigned tolength3.
- A temporary
🗊 Program 14-8
#include <iostream>
#include "FeetInches.h"
using namespace std;
int main()
{
int feet, inches;
FeetInches first, second, third;
cout << "Enter a distance in feet and inches: ";
cin >> feet >> inches;
first.setFeet(feet);
first.setInches(inches);
cout << "Enter another distance in feet and inches: ";
cin >> feet >> inches;
second.setFeet(feet);
second.setInches(inches);
third = first + second;
cout << "first + second = ";
cout << third.getFeet() << " feet, ";
cout << third.getInches() << " inches.\n";
third = first - second;
cout << "first - second = ";
cout << third.getFeet() << " feet, ";
cout << third.getInches() << " inches.\n";
return 0;
}💻 Program Output
Overloading the Prefix ++ Operator
- Unary operators like
++are overloaded without parameters because they only modify the calling object. - The function for the prefix
++operator increments the member, simplifies it, and then returns the modified object (*this).
FeetInches FeetInches::operator++()
{
++inches;
simplify();
return *this;
}- Returning
*thisallows the operator to be used in expressions likedistance2 = ++distance1;.
Overloading the Postfix ++ Operator
- To distinguish the postfix
++operator from the prefix version, its function signature includes a dummy parameter of typeint. - The postfix operator should return the object’s state before it was incremented.
- This is achieved by creating a temporary copy of the object, incrementing the original object, and then returning the temporary copy.
FeetInches FeetInches::operator++(int)
{
FeetInches temp(feet, inches);
inches++;
simplify();
return temp;
}🗊 Program 14-9
#include <iostream>
#include "FeetInches.h"
using namespace std;
int main()
{
int count;
FeetInches first;
FeetInches second(1, 5);
cout << "Demonstrating prefix ++ operator.\n";
for (count = 0; count < 12; count++)
{
first = ++second;
cout << "first: " << first.getFeet() << " feet, ";
cout << first.getInches() << " inches. ";
cout << "second: " << second.getFeet() << " feet, ";
cout << second.getInches() << " inches.\n";
}
cout << "\nDemonstrating postfix ++ operator.\n";
for (count = 0; count < 12; count++)
{
first = second++;
cout << "first: " << first.getFeet() << " feet, ";
cout << first.getInches() << " inches. ";
cout << "second: " << second.getFeet() << " feet, ";
cout << second.getInches() << " inches.\n";
}
return 0;
}💻 Program Output
Checkpoint
14.14 Assume there is a class named
Pet. Write the prototype for a member function ofPetthat overloads the = operator.14.15 Assume
dogandcatare instances of thePetclass, which has overloaded the = operator. Rewrite the following statement so it appears in function call notation instead of operator notation:dog = cat;14.16 What is the disadvantage of an overloaded = operator returning
void?14.17 Describe the purpose of the
thispointer.14.18 The
thispointer is automatically passed to what type of functions?14.19 Assume there is a class named
Animalthat overloads the = and + operators. In the following statement, assumecat,tiger, andwildcatare all instances of theAnimalclass:wildcat = cat + tiger;Of the three objects,
wildcat,cat, ortiger, which is calling theoperator+ function? Which object is passed as an argument into the function?14.20 What does the use of a dummy parameter in a unary operator function indicate to the compiler?
Overloading Relational Operators
- Relational operators (
>,<,==, etc.) can be overloaded to compare class objects. - These operator functions are implemented like other binary operators, but they should always return a
boolvalue (trueorfalse). - The example below shows the function for overloading the
>operator in theFeetInchesclass.
bool FeetInches::operator > (const FeetInches &right)
{
bool status;
if (feet > right.feet)
status = true;
else if (feet == right.feet && inches > right.inches)
status = true;
else
status = false;
return status;
}🗊 Program 14-10
#include <iostream>
#include "FeetInches.h"
using namespace std;
int main()
{
int feet, inches;
FeetInches first, second;
cout << "Enter a distance in feet and inches: ";
cin >> feet >> inches;
first.setFeet(feet);
first.setInches(inches);
cout << "Enter another distance in feet and inches: ";
cin >> feet >> inches;
second.setFeet(feet);
second.setInches(inches);
if (first == second)
cout << "first is equal to second.\n";
if (first > second)
cout << "first is greater than second.\n";
if (first < second)
cout << "first is less than second.\n";
return 0;
}💻 Program Output
💻 Program Output
💻 Program Output
Overloading the << and >> Operators
The stream insertion (
<<) and stream extraction (>>) operators can be overloaded to provide a natural syntax for I/O with class objects.This allows you to write
cout << distance;instead of calling getter functions, andcin >> distance;instead of calling setter functions.These operators must be overloaded as non-member functions (often as
friendfunctions) because the object on the left (coutorcin) is an instance of anostreamoristreamclass, not your class.The function to overload
<<forFeetInches:
ostream &operator << (ostream &strm, const FeetInches &obj)
{
strm << obj.feet << " feet, " << obj.inches << " inches";
return strm;
}Parameters: It takes two parameters: a reference to the stream object (
ostream &strm) and a reference to your class object (const FeetInches &obj).Return Type: It returns a reference to the stream object (
ostream &). This is essential to allow for chaining, such ascout << obj1 << obj2;.The function to overload
>>forFeetInches:
istream &operator >> (istream &strm, FeetInches &obj)
{
cout << "Feet: ";
strm >> obj.feet;
cout << "Inches: ";
strm >> obj.inches;
obj.simplify();
return strm;
}- Since these functions are not members of the
FeetInchesclass, they cannot access its private members (feetandinches). - To grant them access, they must be declared as
friends inside the class declaration.
Contents of FeetInches.h (Version 4)
#ifndef FEETINCHES_H
#define FEETINCHES_H
#include <iostream>
using namespace std;
class FeetInches;
ostream &operator << (ostream &, const FeetInches &);
istream &operator >> (istream &, FeetInches &);
class FeetInches
{
private:
int feet;
int inches;
void simplify();
public:
FeetInches(int f = 0, int i = 0)
{ feet = f;
inches = i;
simplify(); }
void setFeet(int f)
{ feet = f; }
void setInches(int i)
{ inches = i;
simplify(); }
int getFeet() const
{ return feet; }
int getInches() const
{ return inches; }
FeetInches operator + (const FeetInches &);
FeetInches operator - (const FeetInches &);
FeetInches operator ++ ();
FeetInches operator ++ (int);
bool operator > (const FeetInches &);
bool operator < (const FeetInches &);
bool operator == (const FeetInches &);
friend ostream &operator << (ostream &, const FeetInches &);
friend istream &operator >> (istream &, FeetInches &);
};
#endif🗊 Program 14-11
#include <iostream>
#include "FeetInches.h"
using namespace std;
int main()
{
FeetInches first, second;
cout << "Enter a distance in feet and inches.\n";
cin >> first;
cout << "Enter another distance in feet and inches.\n";
cin >> second;
cout << "The values you entered are:\n";
cout << first << " and " << second << endl;
return 0;
}💻 Program Output
Overloading the [ ] Operator
- The subscript operator (
[]) can be overloaded to give a class array-like behavior. - This is useful for creating custom array classes that can perform actions like bounds checking, which standard C++ arrays do not.
- The
IntArrayclass below is an example of an array class with built-in bounds checking.
Contents of IntArray.h
Contents of IntArray.cpp
#include <iostream>
#include <cstdlib>
#include "IntArray.h"
using namespace std;
IntArray::IntArray(int s)
{
arraySize = s;
aptr = new int [s];
for (int count = 0; count < arraySize; count++)
*(aptr + count) = 0;
}
IntArray::IntArray(const IntArray &obj)
{
arraySize = obj.arraySize;
aptr = new int [arraySize];
for(int count = 0; count < arraySize; count++)
*(aptr + count) = *(obj.aptr + count);
}
IntArray::~IntArray()
{
if (arraySize > 0)
delete [] aptr;
}
void IntArray::subscriptError()
{
cout << "ERROR: Subscript out of range.\n";
exit(0);
}
const IntArray IntArray::operator=(const IntArray &right)
{
if (this != &right)
{
delete[] aptr;
arraySize = right.arraySize;
aptr = new int[arraySize];
for (int count = 0; count < arraySize; count++)
*(aptr + count) = *(right.aptr + count);
}
return *this;
}
int &IntArray::operator[](const int &sub)
{
if (sub < 0 || sub >= arraySize)
subscriptError();
return aptr[sub];
}- The
operator[]function takes a single parameter, which is the value inside the brackets (the subscript).
int &IntArray::operator[](const int &sub)
{
if (sub < 0 || sub >= arraySize)
subscriptError();
return aptr[sub];
}- Inside the function, it first checks if the subscript
subis within the valid range. If not, an error function is called. - Crucially, the function must return a reference (
int &) to the array element. - Returning a reference makes the expression an lvalue, which means it represents a modifiable memory location. This allows the overloaded operator to be used on the left side of an assignment, as in
table[5] = 27;. - If it returned a simple
int, it would be an rvalue, and assignment would not be possible.
🗊 Program 14-12
#include <iostream>
#include "IntArray.h"
using namespace std;
int main()
{
const int SIZE = 10;
IntArray table(SIZE);
for (int x = 0; x < SIZE; x++)
table[x] = (x * 2);
for (int x = 0; x < SIZE; x++)
cout << table[x] << " ";
cout << endl;
for (int x = 0; x < SIZE; x++)
table[x] = table[x] + 5;
for (int x = 0; x < SIZE; x++)
cout << table[x] << " ";
cout << endl;
for (int x = 0; x < SIZE; x++)
table[x]++;
for (int x = 0; x < SIZE; x++)
cout << table[x] << " ";
cout << endl;
return 0;
}💻 Program Output
🗊 Program 14-13
#include <iostream>
#include "IntArray.h"
using namespace std;
int main()
{
const int SIZE = 10;
IntArray table(SIZE);
for (int x = 0; x < SIZE; x++)
table[x] = x;
for (int x = 0; x < SIZE; x++)
cout << table[x] << " ";
cout << endl;
cout << "Now attempting to use an invalid subscript.\n";
table[SIZE + 1] = 0;
return 0;
}💻 Program Output
Checkpoint
14.21 Describe the values that should be returned from functions that overload relational operators.
14.22 What is the advantage of overloading the
<<and>>operators?14.23 What type of object should an overloaded
<<operator function return?14.24 What type of object should an overloaded
>>operator function return?14.25 If an overloaded
<<or>>operator accesses a private member of a class, what must be done in that class’s declaration?14.26 Assume the class
NumListhas overloaded the[]operator. In the expression below,list1is an instance of theNumListclass:list1[25]Rewrite the expression above to explicitly call the function that overloads the
[]operator.
14.6 Object Conversion
Concept:
- You can write special operator functions to define automatic type conversions from a class object to another data type.
Just as C++ automatically converts between built-in types (e.g.,
inttodouble), you can enable similar automatic conversions for your class objects.This is achieved by writing a special type of operator function.
The function to convert a
FeetInchesobject to adoubleis shown below:
FeetInches::operator double()
{
double temp = feet;
temp += (inches / 12.0);
return temp;
}Function Header: The function name is
operatorfollowed by the destination type (double).No Return Type: The return type is not specified in the header because it is implied by the function’s name. It will always return a
double.No Parameters: Conversion operators typically do not take parameters.
With this function defined, a
FeetInchesobject can be automatically converted to adouble:
d = distance; 🗊 Program 14-14
#include <iostream>
#include "FeetInches.h"
using namespace std;
int main()
{
double d;
int i;
FeetInches distance;
cout << "Enter a distance in feet and inches:\n";
cin >> distance;
d = distance;
i = distance;
cout << "The value " << distance;
cout << " is equivalent to " << d << " feet\n";
cout << "or " << i << " feet, rounded down.\n";
return 0;
}💻 Program Output
Checkpoint
14.27 When overloading a binary operator such as + or –, what object is passed into the operator function’s parameter?
14.28 Explain why overloaded prefix and postfix ++ and –– operator functions should return a value.
14.29 How does C++ tell the difference between an overloaded prefix and postfix ++ or –– operator function?
14.30 Write member functions of the
FeetInchesclass that overload the prefix and postfix –– operators. Demonstrate the functions in a simple program similar to Program 14-14.
14.7 Aggregation
Concept:
- Aggregation is when a class contains an instance of another class as one of its members.
Class Aggregation
In software design, it’s often useful to build complex objects from simpler ones, just like real-world objects are made of smaller parts.
This creates a “has a” relationship. For example, a
Courseobject “has an”Instructorobject and “has a”TextBookobject.This is also known as a whole-part relationship, where one class (the whole) is composed of other classes (the parts).
Using aggregation helps to separate related data into distinct, manageable classes.
The
Instructorclass holds instructor-related data.
Contents of Instructor.h
#ifndef INSTRUCTOR
#define INSTRUCTOR
#include <iostream>
#include <string>
using namespace std;
class Instructor
{
private:
string lastName;
string firstName;
string officeNumber;
public:
Instructor()
{ set("", "", ""); }
Instructor(string lname, string fname, string office)
{ set(lname, fname, office); }
void set(string lname, string fname, string office)
{ lastName = lname;
firstName = fname;
officeNumber = office; }
void print() const
{ cout << "Last name: " << lastName << endl;
cout << "First name: " << firstName << endl;
cout << "Office number: " << officeNumber << endl; }
};
#endif- The
TextBookclass holds textbook-related data.
Contents of TextBook.h
#ifndef TEXTBOOK
#define TEXTBOOK
#include <iostream>
#include <string>
using namespace std;
class TextBook
{
private:
string title;
string author;
string publisher;
public:
TextBook()
{ set("", "", ""); }
TextBook(string textTitle, string auth, string pub)
{ set(textTitle, auth, pub); }
void set(string textTitle, string auth, string pub)
{ title = textTitle;
author = auth;
publisher = pub; }
void print() const
{ cout << "Title: " << title << endl;
cout << "Author: " << author << endl;
cout << "Publisher: " << publisher << endl; }
};
#endif- The
Courseclass is an aggregate class because it containsInstructorandTextBookobjects as members.
Contents of Course.h
#ifndef COURSE
#define COURSE
#include <iostream>
#include <string>
#include "Instructor.h"
#include "TextBook.h"
using namespace std;
class Course
{
private:
string courseName;
Instructor instructor;
TextBook textbook;
public:
Course(string course, string instrLastName,
string instrFirstName, string instrOffice,
string textTitle, string author,
string publisher)
{
courseName = course;
instructor.set(instrLastName, instrFirstName, instrOffice);
textbook.set(textTitle, author, publisher); }
void print() const
{ cout << "Course name: " << courseName << endl << endl;
cout << "Instructor Information:\n";
instructor.print();
cout << "\nTextbook Information:\n";
textbook.print();
cout << endl;}
};
#endif🗊 Program 14-15
💻 Program Output
Using Member Initialization Lists with Aggregate Classes
A constructor’s member initialization list can be used to call the constructors for its member objects.
This is an alternative and often more efficient way to initialize member objects.
The
Courseconstructor rewritten with a member initialization list:
- In the initialization list, you call the constructors for the member objects (
instructorandtextbook), passing the required arguments.
Aggregation in UML Diagrams
- In a UML diagram, aggregation is shown with a line connecting two classes, with an open diamond at the end next to the aggregate (or “whole”) class.
14.8 Focus on Object-Oriented Design: Class Collaborations
Concept:
- In object-oriented programming, it’s common for classes to interact or collaborate to accomplish tasks.
- Identifying these collaborations is a key part of the design process.
In many applications, objects of different classes must work together.
An object might require the services of another object to fulfill its own responsibilities.
For a collaboration to occur, one object must know about the other object’s public member functions and how to call them.
The
Stockclass below is an example. It holds a stock’s tradingsymboland itssharePrice.
Contents of Stock.h
#ifndef STOCK
#define STOCK
#include <string>
using namespace std;
class Stock
{
private:
string symbol;
double sharePrice;
public:
Stock()
{ set("", 0.0);}
Stock(const string sym, double price)
{ set(sym, price); }
Stock(const Stock &obj)
{ set(obj.symbol, obj.sharePrice); }
void set(string sym, double price)
{ symbol = sym;
sharePrice = price; }
string getSymbol() const
{ return symbol; }
double getSharePrice() const
{ return sharePrice; }
};
#endif- The
StockPurchaseclass collaborates with theStockclass to simulate buying a stock. - To calculate the cost of a purchase, a
StockPurchaseobject needs to get the share price from aStockobject. This requires calling theStockclass’sgetSharePricemethod.
Contents of StockPurchase.h
#ifndef STOCK_PURCHASE
#define STOCK_PURCHASE
#include "Stock.h"
class StockPurchase
{
private:
Stock stock;
int shares;
public:
StockPurchase()
{ shares = 0;}
StockPurchase(const Stock &stockObject, int numShares)
{ stock = stockObject;
shares = numShares; }
double getCost() const
{ return shares * stock.getSharePrice(); }
};
#endif- The
StockPurchaseclass demonstrates two collaborations with theStockclass:- Its constructor accepts a
Stockobject and uses theStockclass’s copy constructor to create a copy. - Its
getCostfunction calls thestockobject’sgetSharePricemethod to calculate the total cost.
- Its constructor accepts a
🗊 Program 14-16
#include <iostream>
#include <iomanip>
#include "Stock.h"
#include "StockPurchase.h"
using namespace std;
int main()
{
int sharesToBuy;
Stock xyzCompany("XYZ", 9.62);
cout << setprecision(2) << fixed << showpoint;
cout << "XYZ Company's trading symbol is "
<< xyzCompany.getSymbol() << endl;
cout << "The stock is currently $"
<< xyzCompany.getSharePrice()
<< " per share.\n";
cout << "How many shares do you want to buy? ";
cin >> sharesToBuy;
StockPurchase buy(xyzCompany, sharesToBuy);
cout << "The cost of the transaction is $"
<< buy.getCost() << endl;
return 0;
}💻 Program Output
Determining Class Collaborations with CRC Cards
- During design, you can identify necessary collaborations by examining class responsibilities.
- A class’s responsibilities include what it needs to know and what it needs to do.
- A popular tool for this is the CRC card, which stands for Class, Responsibilities, and Collaborations.
- To create CRC cards:
- Use one index card for each class.
- Write the class name at the top.
- Create two columns: one for responsibilities and one for collaborations.
- For each responsibility, ask if the class needs to interact with another class to fulfill it.
- If collaboration is needed, write the name of the collaborating class in the right column.
- The CRC card for
StockPurchaseshows it must collaborate with theStockclass to know which stock to purchase and to calculate the cost. - Completing CRC cards for all classes gives a clear overview of how the classes in an application must interact.
Checkpoint
14.31 What are the benefits of having operator functions that perform object conversion?
14.32 Why are no return types listed in the prototypes or headers of operator functions that perform data type conversion?
14.33 Assume there is a class named
BlackBox. Write the header for a member function that converts aBlackBoxobject to anint.14.34 Assume there are two classes,
BigandSmall. TheBigclass has, as a member, an instance of theSmallclass. Write a sentence that describes the relationship between the two classes.
14.9 Focus on Object-Oriented Programming: Simulating the Game of Cho-Han
Cho-Han is a Japanese dice game where players bet on whether the sum of two rolled dice is even (Cho) or odd (Han).
We will simulate a simplified version of this game with a dealer and two players.
The simulation will run for five rounds, with points awarded for correct guesses.
The program will use three main classes:
Dieclass: From Chapter 13, used to simulate two six-sided dice.Dealerclass: Rolls the dice and determines if the result is Cho or Han.Playerclass: Represents a player who can make a guess and accumulate points.
First, here is the
Dealerclass. It uses twoDieobjects.
Contents of Dealer.h
Contents of Dealer.cpp
#include "Dealer.h"
#include "Die.h"
#include <string>
using namespace std;
Dealer::Dealer()
{
die1Value = 0;
die2Value = 0;
}
void Dealer::rollDice()
{
die1.roll();
die2.roll();
die1Value = die1.getValue();
die2Value = die2.getValue();
}
string Dealer::getChoOrHan()
{
string result;
int sum = die1Value + die2Value;
if (sum % 2 == 0)
result = "Cho (even)";
else
result = "Han (odd)";
return result;
}
int Dealer::getDie1Value()
{
return die1Value;
}
int Dealer::getDie2Value()
{
return die2Value;
}- Next is the
Playerclass, which stores a player’s name, their guess, and their point total.
Contents of Player.h
Contents of Player.cpp
#include "Player.h"
#include <cstdlib>
#include <ctime>
#include <string>
using namespace std;
Player::Player(string playerName)
{
srand(time(0));
name = playerName;
guess = "";
points = 0;
}
void Player::makeGuess()
{
const int MIN_VALUE = 0;
const int MAX_VALUE = 1;
int guessNumber;
guessNumber = (rand() % (MAX_VALUE - MIN_VALUE + 1)) + MIN_VALUE;
if (guessNumber == 0)
guess = "Cho (even)";
else
guess = "Han (odd)";
}
void Player::addPoints(int newPoints)
{
points += newPoints;
}
string Player::getName()
{
return name;
}
string Player::getGuess()
{
return guess;
}
int Player::getPoints()
{
return points;
}- The
mainfunction orchestrates the game, playing five rounds and then declaring a winner.
🗊 Program 14-17
#include <iostream>
#include <string>
#include "Dealer.h"
#include "Player.h"
using namespace std;
void roundResults(Dealer &, Player &, Player &);
void checkGuess(Player &, Dealer &);
void displayGrandWinner(Player, Player);
int main()
{
const int MAX_ROUNDS = 5;
string player1Name;
string player2Name;
cout << "Enter the first player's name: ";
cin >> player1Name;
cout << "Enter the second player's name: ";
cin >> player2Name;
Dealer dealer;
Player player1(player1Name);
Player player2(player2Name);
for (int round = 0; round < MAX_ROUNDS; round++)
{
cout << "----------------------------\n";
cout << "Now playing round " << (round + 1)
<< endl;
dealer.rollDice();
player1.makeGuess();
player2.makeGuess();
roundResults(dealer, player1, player2);
}
displayGrandWinner(player1, player2);
return 0;
}
void roundResults(Dealer &dealer, Player &player1, Player &player2)
{
cout << "The dealer rolled " << dealer.getDie1Value()
<< " and " << dealer.getDie2Value() << endl;
cout << "Result: " << dealer.getChoOrHan() << endl;
checkGuess(player1, dealer);
checkGuess(player2, dealer);
}
void checkGuess(Player &player, Dealer &dealer)
{
const int POINTS_TO_ADD = 1;
string guess = player.getGuess();
string choHanResult = dealer.getChoOrHan();
cout << "The player " << player.getName()
<< " guessed " << player.getGuess() << endl;
if (guess == choHanResult)
{
player.addPoints(POINTS_TO_ADD);
cout << "Awarding " << POINTS_TO_ADD
<< " point(s) to " << player.getName()
<< endl;
}
}
void displayGrandWinner(Player player1, Player player2)
{
cout << "----------------------------\n";
cout << "Game over. Here are the results:\n";
cout << player1.getName() << ": "
<< player1.getPoints() << " points\n";
cout << player2.getName() << ": "
<< player2.getPoints() << " points\n";
if (player1.getPoints() > player2.getPoints())
{
cout << player1.getName()
<< " is the grand winner!\n";
}
else if (player2.getPoints() > player1.getPoints())
{
cout << player2.getName()
<< " is the grand winner!\n";
}
else
{
cout << "Both players are tied!\n";
}
}💻 Program Output
- The program uses several helper functions to manage the game flow:
main: Gets player names, creates theDealerandPlayerobjects, and runs the main game loop for five rounds.roundResults: Displays the dealer’s roll and the Cho/Han result, then callscheckGuessfor each player.checkGuess: Compares a player’s guess to the dealer’s result and awards points if they match.displayGrandWinner: Shows the final scores and declares the winner or a tie.
14.10 Rvalue References and Move Semantics
Concept:
- A move operation efficiently transfers ownership of resources (like dynamically allocated memory) from a source object to a target object.
- It is most useful when the source object is a temporary value that is about to be destroyed.
- C++11 introduced features like move semantics to improve performance, particularly for assignment operations and constructors.
- Understanding move semantics requires first understanding rvalue references.
Lvalues and Rvalues
- During program execution, values in memory can be categorized into two types:
- lvalues: Persistent values that have a name and can be accessed across multiple statements. They can appear on the left side of an assignment operator. Think of variables like
int x;. - rvalues: Temporary, unnamed values that exist only for the duration of the statement that created them. They cannot appear on the left side of an assignment. Examples include the result of an expression like
2 * 6or the return value of a function.
- lvalues: Persistent values that have a name and can be accessed across multiple statements. They can appear on the left side of an assignment operator. Think of variables like
Tip:
- A simple way to think of an lvalue is as anything that can be on the left side of an assignment operator.
Rvalue References
- A standard reference variable (e.g.,
int &ref) is an lvalue reference and can only refer to lvalues. - C++11 introduced the rvalue reference (e.g.,
int &&rvalRef), which can refer only to temporary rvalues. - The syntax for an rvalue reference uses a double ampersand (
&&). - An rvalue reference effectively gives a name to a temporary object, turning it into an lvalue and extending its lifetime.
int &&rvalRef = square(5);
cout << rvalRef << endl; - A key consequence is that a temporary object can have at most one rvalue reference pointing to it, ensuring that only one part of the program has access to it. This is vital for move operations.
Move Semantics
Operations like passing objects to functions or returning them from functions can involve a lot of inefficient memory allocation and copying, especially for classes that manage dynamic memory.
Move semantics avoids this expensive copying by “stealing” or transferring ownership of the underlying resources from the source object to the destination object.
This is safe and efficient because the source is a temporary rvalue that is about to be destroyed anyway.
The
Personclass below demonstrates the overhead of copying.
Contents of Person.h
#ifndef PERSON_H
#define PERSON_H
#include <iostream>
#include <cstring>
using namespace std;
class Person
{
private:
char *name;
public:
Person()
{ cout << "*** default constructor ***\n";
name = nullptr; }
Person(char *n)
{ cout << "*** parameterized constructor ***\n";
name = new char[strlen(n) + 1];
strcpy(name, n); }
Person(const Person &obj)
{ cout << "*** copy constructor ***\n";
name = new char[strlen(obj.name) + 1];
strcpy(name, obj.name); }
~Person()
{ cout << "*** destructor ***\n";
delete [] name; }
Person & operator=(const Person &right)
{ cout << "*** assignment operator ***\n";
if (this != &right)
{
if (name != nullptr)
delete[] name;
name = new char[strlen(right.name) + 1];
strcpy(name, right.name);
}
return *this; }
char *getName() const
{ return name; }
};
#endif🗊 Program 14-18
#include <iostream>
#include <string>
#include "Person.h"
using namespace std;
Person makePerson();
void displayPerson(Person);
int main()
{
Person person;
person = makePerson();
displayPerson(person);
return 0;
}
Person makePerson()
{
Person p("Will MacKenzie");
return p;
}
void displayPerson(Person p2)
{
cout << p2.getName() << endl;
}💻 Program Output
The program’s output shows numerous constructor, destructor, and assignment operator calls, highlighting the overhead of creating, copying, and destroying temporary objects.
To optimize this, we can add a move assignment operator and a move constructor.
Move Assignment Operator: This operator takes an rvalue reference parameter. Instead of deep copying, it swaps the internal pointers of the source and destination objects.
Person& operator=(Person&& right)
{ if (this != &right)
{
swap(name, right.name);
}
return *this;
}- Move Constructor: This constructor also takes an rvalue reference. It “steals” the pointer from the temporary source object and then nullifies the source’s pointer to prevent it from being deleted by the source’s destructor.
Person(Person&& temp)
{
name = temp.name;
temp.name = nullptr;
}When to Implement Move Semantics in a Class
- You should implement move semantics (a move constructor and a move assignment operator) whenever your class contains a pointer or a handle to an external resource, such as dynamically allocated memory.
- This ensures optimal performance by avoiding unnecessary copies when working with temporary objects.
Default Operations Provided by the Compiler
- The C++ compiler can automatically generate default versions of five special member functions if you don’t provide them:
- Default constructor
- Copy constructor
- Copy assignment operator
- Move constructor
- Move assignment operator
- Destructor
- However, if you manually define any of these, the compiler will not generate any of the others.
- This is often called the “Rule of Five”: if you need to write one of these five special members, you should probably write all five to ensure correct resource management.
Review Questions and Exercises
Short Answer
Describe the difference between an instance member variable and a static member variable.
Assume a class named
Numbershas the following static member function declaration:static void showTotal();Write a statement that calls the
showTotalfunction.A static member variable is declared in a class. Where is the static member variable defined?
What is a friend function?
Why is it not always a good idea to make an entire class a friend of another class?
What is memberwise assignment?
When is a copy constructor called?
How can the compiler determine if a constructor is a copy constructor?
Describe a situation where memberwise assignment is not desirable.
Why must the parameter of a copy constructor be a reference?
What is a default copy constructor?
Why would a programmer want to overload operators rather than use regular member functions to perform similar operations?
What is passed to the parameter of a class’s
operator=function?Why shouldn’t a class’s overloaded = operator be implemented with a
voidoperator function?How does the compiler know whether an overloaded ++ operator should be used in prefix or postfix mode?
What is the
thispointer?What type of value should be returned from an overloaded relational operator function?
The class
Stuffhas both a copy constructor and an overloaded = operator. Assumeblobandclumpare both instances of theStuffclass. For each statement below, indicate whether the copy constructor or the overloaded = operator will be called:Stuff blob = clump; clump = blob; blob.operator=(clump); showValues(blob);Explain the programming steps necessary to make a class’s member variable static.
Explain the programming steps necessary to make a class’s member function static.
Consider the following class declaration:
class Thing { private: int x; int y; static int z; public: Thing() { x = y = z; } static void putThing(int a) { z = a; } };Assume a program containing the class declaration defines three
Thingobjects with the following statement:Thing one, two, three;How many separate instances of the
xmember exist?How many separate instances of the
ymember exist?How many separate instances of the
zmember exist?What value will be stored in the
xandymembers of each object?Write a statement that will call the
PutThingmember function before the objects above are defined.Describe the difference between making a class a member of another class (object aggregation), and making a class a friend of another class.
What is the purpose of a forward declaration of a class?
Explain why memberwise assignment can cause problems with a class that contains a pointer member.
Why is a class’s copy constructor called when an object of that class is passed by value into a function?
Fill-in-the-Blank
If a member variable is declared ____________________, all objects of that class have access to the same variable.
Static member variables are defined ____________________ the class.
A(n) ____________________ member function cannot access any nonstatic member variables in its own class.
A static member function may be called ____________________ any instances of its class are defined.
A(n) ____________________ function is not a member of a class, but has access to the private members of the class.
A(n) ____________________ tells the compiler that a specific class will be declared later in the program.
____________________ is the default behavior when an object is assigned the value of another object of the same class.
A(n) ____________________ is a special constructor, called whenever a new object is initialized with another object’s data.
____________________ is a special built-in pointer that is automatically passed as a hidden argument to all nonstatic member functions.
An operator may be ____________________ to work with a specific class.
When overloading the ____________________ operator, its function must have a dummy parameter.
Making an instance of one class a member of another class is called ____________________.
Object aggregation is useful for creating a(n) ____________________ relationship between two classes.
Algorithm Workbench
Assume a class named
Birdexists. Write the header for a member function that overloads the = operator for that class.Assume a class named
Dollarsexists. Write the headers for member functions that overload the prefix and postfix ++ operators for that class.Assume a class named
Yenexists. Write the header for a member function that overloads the < operator for that class.Assume a class named Length exists. Write the header for a member function that overloads cout’s << operator for that class.
Assume a class named Collection exists. Write the header for a member function that overloads the [] operator for that class.
True or False
T F Static member variables cannot be accessed by nonstatic member functions.
T F Static member variables are defined outside their class declaration.
T F A static member function may refer to nonstatic member variables of the same class, but only after an instance of the class has been defined.
T F When a function is declared a
friendby a class, it becomes a member of that class.T F A
friendfunction has access to the private members of the class declaring it afriend.T F An entire class may be declared a
friendof another class.T F In order for a function or class to become a friend of another class, it must be declared as such by the class granting it access.
T F If a class has a pointer as a member, it’s a good idea to also have a copy constructor.
T F You cannot use the = operator to assign one object’s values to another object, unless you overload the operator.
T F If a class doesn’t have a copy constructor, the compiler generates a default copy constructor for it.
T F If a class has a copy constructor, and an object of that class is passed by value into a function, the function’s parameter will not call its copy constructor.
T F The
thispointer is passed to static member functions.T F All functions that overload unary operators must have a dummy parameter.
T F For an object to perform automatic type conversion, an operator function must be written.
T F It is possible to have an instance of one class as a member of another class.
Find the Errors
Each of the following class declarations has errors. Locate as many as you can.
-
class Box { private: double width; double length; double height; public: Box(double w, l, h) { width = w; length = l; height = h; } Box(Box b) { width = b.width; length = b.length; height = b.height; } ... Other member functions follow ... }; -
class Circle { private: double diameter; int centerX; int centerY; public: Circle(double d, int x, int y) { diameter = d; centerX = x; centerY = y; } void Circle=(Circle &right) { diameter = right.diameter; centerX = right.centerX; centerY = right.centerY; } ... Other member functions follow ... }; -
class Point { private: int xCoord; int yCoord; public: Point (int x, int y) { xCoord = x; yCoord = y; } void operator+(const &Point right) { xCoord += right.xCoord; yCoord += right.yCoord; } ... Other member functions follow ... }; -
class Box { private: double width; double length; double height; public: Box(double w, l, h) { width = w; length = l; height = h; } void operator++() { ++width; ++length;} void operator++() { width++; length++;} ... Other member functions follow ... }; -
class Yard { private: float length; public: yard(float l) { length = l; } void operator float() { return length; } ... Other member functions follow ... };
Programming Challenges
NumbersClassDesign a class
Numbersthat can be used to translate whole dollar amounts in the range 0 through 9999 into an English description of the number. For example, the number 713 would be translated into the string seven hundred thirteen, and 8203 would be translated into eight thousand two hundred three. The class should have a single integer member variable:int number;and a static array of
stringobjects that specify how to translate key dollar amounts into the desired format. For example, you might use static strings such asstring lessThan20[20] = {"zero", "one", ..., "eighteen", "nineteen"}; string hundred = "hundred"; string thousand = "thousand";The class should have a constructor that accepts a nonnegative integer and uses it to initialize the
Numbersobject. It should have a member functionprint()that prints the English description of theNumbersobject. Demonstrate the class by writing a main program that asks the user to enter a number in the proper range then prints out its English description.Day of the Year
Assuming a year has 365 days, write a class named
DayOfYearthat takes an integer representing a day of the year and translates it to a string consisting of the month followed by day of the month. For example,Day 2 would be January 2.
Day 32 would be February 1.
Day 365 would be December 31.
The constructor for the class should take as parameter an integer representing the day of the year, and the class should have a member function
print()that prints the day in the month–day format. The class should have an integer member variable to represent the day, and should have static member variables holdingstringobjects that can be used to assist in the translation from the integer format to the month–day format.Test your class by inputting various integers representing days and printing out their representation in the month–day format.
Day of the Year Modification
Modify the
DayOfYearclass, written in Programming Challenge 2, to add a constructor that takes two parameters: astringobject representing a month and an integer in the range 0 through 31 representing the day of the month. The constructor should then initialize the integer member of the class to represent the day specified by the month and day of month parameters. The constructor should terminate the program with an appropriate error message if the number entered for a day is outside the range of days for the month given.Add the following overloaded operators:
++prefix and postfix increment operators. These operators should modify the DayOfYearobject so it represents the next day. If the day is already the end of the year, the new value of the object will represent the first day of the year.--prefix and postfix decrement operators. These operators should modify the DayOfYearobject so it represents the previous day. If the day is already the first day of the year, the new value of the object will represent the last day of the year.NumDaysClass
Solving the NumDays Problem
Design a class called
NumDays. The class’s purpose is to store a value that represents a number of work hours and convert it to a number of days. For example, 8 hours would be converted to 1 day, 12 hours would be converted to 1.5 days, and 18 hours would be converted to 2.25 days. The class should have a constructor that accepts a number of hours, as well as member functions for storing and retrieving the hours and days. The class should also have the following overloaded operators:+Addition operator. When two NumDaysobjects are added together, the overloaded + operator should return the sum of the two objects’ hours members.-Subtraction operator. When one NumDaysobject is subtracted from another, the overloaded-operator should return the difference of the two objects’ hours members.++Prefix and postfix increment operators. These operators should increment the number of hours stored in the object. When incremented, the number of days should be automatically recalculated. --Prefix and postfix decrement operators. These operators should decrement the number of hours stored in the object. When decremented, the number of days should be automatically recalculated. Time Off
Note:This assignment assumes you have already completed Programming Challenge 4.
Design a class named
TimeOff. The purpose of the class is to track an employee’s sick leave, vacation, and unpaid time off. It should have, as members, the following instances of theNumDaysclass described in Programming Challenge 4:maxSickDaysA NumDaysobject that records the maximum number of days of sick leave the employee may take.sickTakenA NumDaysobject that records the number of days of sick leave the employee has already taken.maxVacationA NumDaysobject that records the maximum number of days of paid vacation the employee may take.vacTakenA NumDaysobject that records the number of days of paid vacation the employee has already taken.maxUnpaidA NumDaysobject that records the maximum number of days of unpaid vacation the employee may take.unpaidTakenA NumDaysobject that records the number of days of unpaid leave the employee has taken.Additionally, the class should have members for holding the employee’s name and identification number. It should have an appropriate constructor and member functions for storing and retrieving data in any of the member objects.
Input Validation: Company policy states that an employee may not accumulate more than 240 hours of paid vacation. The class should not allow the
maxVacationobject to store a value greater than this amount.Personnel Report
Note:This assignment assumes you have already completed Programming Challenges 4 and 5.
Write a program that uses an instance of the
TimeOffclass you designed in Programming Challenge 5. The program should ask the user to enter the number of months an employee has worked for the company. It should then use theTimeOffobject to calculate and display the employee’s maximum number of sick leave and vacation days. Employees earn 12 hours of vacation leave and 8 hours of sick leave per month.MonthClassDesign a class named
Month. The class should have the following private members:nameA stringobject that holds the name of a month, such as “January,” “February,” and so on.monthNumberAn integer variable that holds the number of the month. For example, January would be 1, February would be 2, and so on. Valid values for this variable are 1 through 12. In addition, provide the following member functions:
A default constructor that sets
monthNumberto 1 andnameto “January.”A constructor that accepts the name of the month as an argument. It should set
nameto the value passed as the argument and setmonthNumberto the correct value.A constructor that accepts the number of the month as an argument. It should set
monthNumberto the value passed as the argument and setnameto the correct month name.Appropriate set and get functions for the
nameandmonthNumbermember variables.Prefix and postfix overloaded ++ operator functions that increment
monthNumberand set name to the name of next month. IfmonthNumberis set to 12 when these functions execute, they should setmonthNumberto 1 andnameto “January.”Prefix and postfix overloaded
--operator functions that decrementmonthNumberand setnameto the name of previous month. IfmonthNumberis set to 1 when these functions execute, they should setmonthNumberto 12 andnameto “December.”
Also, you should overload
cout’s<<operator andcin’s>>operator to work with theMonthclass. Demonstrate the class in a program.DateClass ModificationModify the
Dateclass in Programming Challenge 1 of Chapter 13. The new version should have the following overloaded operators:++Prefix and postfix increment operators. These operators should increment the object’s daymember.--Prefix and postfix decrement operators. These operators should decrement the object’s daymember.-Subtraction operator. If one Dateobject is subtracted from another, the operator should give the number of days between the two dates. For example, if April 10, 2014 is subtracted from April 18, 2014, the result will be 8.<<cout’s stream insertion operator. This operator should cause the date to be displayed in the formApril 18, 2018>>cin’s stream extraction operator. This operator should prompt the user for a date to be stored in aDateobject.The class should detect the following conditions and handle them accordingly:
When a date is set to the last day of the month and incremented, it should become the first day of the following month.
When a date is set to December 31 and incremented, it should become January 1 of the following year.
When a day is set to the first day of the month and decremented, it should become the last day of the previous month.
When a date is set to January 1 and decremented, it should become December 31 of the previous year.
Demonstrate the class’s capabilities in a simple program.
Input Validation: The overloaded
>>operator should not accept invalid dates. For example, the date 13/45/2018 should not be accepted.FeetInchesModificationModify the
FeetInchesclass discussed in this chapter so it overloads the following operators:<= >= !=Demonstrate the class’s capabilities in a simple program.
Corporate Sales
A corporation has six divisions, each responsible for sales to different geographic locations. Design a
DivSalesclass that keeps sales data for a division, with the following members:An array with four elements for holding four quarters of sales figures for the division.
A private static variable for holding the total corporate sales for all divisions for the entire year.
A member function that takes four arguments, each assumed to be the sales for a quarter. The value of the arguments should be copied into the array that holds the sales data. The total of the four arguments should be added to the static variable that holds the total yearly corporate sales.
A function that takes an integer argument within the range of 0–3. The argument is to be used as a subscript into the division quarterly sales array. The function should return the value of the array element with that subscript.
Write a program that creates an array of six
DivSalesobjects. The program should ask the user to enter the sales for four quarters for each division. After the data are entered, the program should display a table showing the division sales for each quarter. The program should then display the total corporate sales for the year.Input Validation: Only accept positive values for quarterly sales figures.
FeetInchesClass Copy Constructor andmultiplyFunctionAdd a copy constructor to the
FeetInchesclass. This constructor should accept aFeetInchesobject as an argument. The constructor should assign to thefeetattribute the value in the argument’sfeetattribute, and assign to theinchesattribute the value in the argument’sinchesattribute. As a result, the new object will be a copy of the argument object.Next, add a
multiplymember function to theFeetInchesclass. Themultiplyfunction should accept aFeetInchesobject as an argument. The argument object’sfeetandinchesattributes will be multiplied by the calling object’sfeetandinchesattributes, and aFeetInchesobject containing the result will be returned.LandTractClassMake a
LandTractclass that is composed of twoFeetInchesobjects: one for the tract’s length, and one for the width. The class should have a member function that returns the tract’s area. Demonstrate the class in a program that asks the user to enter the dimensions for two tracts of land. The program should display the area of each tract of land, and indicate whether the tracts are of equal size.Carpet Calculator
The Westfield Carpet Company has asked you to write an application that calculates the price of carpeting for rectangular rooms. To calculate the price, you multiply the area of the floor (width times length) by the price per square foot of carpet. For example, the area of floor that is 12 feet long and 10 feet wide is 120 square feet. To cover that floor with carpet that costs $8 per square foot would cost $960. (12 × 10 × 8 = 960.)
First, you should create a class named
RoomDimensionthat has twoFeetInchesobjects as attributes: one for the length of the room, and one for the width. (You should use the version of theFeetInchesclass you created in Programming Challenge 11 with the addition of amultiplymember function. You can use this function to calculate the area of the room.) TheRoomDimensionclass should have a member function that returns the area of the room as aFeetInchesobject.Next, you should create a
RoomCarpetclass that has aRoomDimensionobject as an attribute. It should also have an attribute for the cost of the carpet per square foot. TheRoomCarpetclass should have a member function that returns the total cost of the carpet.Once you have written these classes, use them in an application that asks the user to enter the dimensions of a room and the price per square foot of the desired carpeting. The application should display the total cost of the carpet.
Parking Ticket Simulator
For this assignment, you will design a set of classes that work together to simulate a police officer issuing a parking ticket. The classes you should design are:
The
ParkedCarClass: This class should simulate a parked car. The class’s responsibilities are:- To know the car’s make, model, color, license number, and the number of minutes that the car has been parked
The
ParkingMeterClass: This class should simulate a parking meter. The class’s only responsibility is:- To know the number of minutes of parking time that has been purchased
The
ParkingTicketClass: This class should simulate a parking ticket. The class’s responsibilities are:To report the make, model, color, and license number of the illegally parked car
To report the amount of the fine, which is $25 for the first hour or part of an hour that the car is illegally parked, plus $10 for every additional hour or part of an hour that the car is illegally parked
To report the name and badge number of the police officer issuing the ticket
The
PoliceOfficerClass: This class should simulate a police officer inspecting parked cars. The class’s responsibilities are:To know the police officer’s name and badge number
To examine a
ParkedCarobject and aParkingMeterobject, and determine whether the car’s time has expiredTo issue a parking ticket (generate a
ParkingTicketobject) if the car’s time has expired
Write a program that demonstrates how these classes collaborate.
Car Instrument Simulator
For this assignment you will design a set of classes that work together to simulate a car’s fuel gauge and odometer. The classes you will design are:
The
FuelGaugeClass: This class will simulate a fuel gauge. Its responsibilities areTo know the car’s current amount of fuel, in gallons.
To report the car’s current amount of fuel, in gallons.
To be able to increment the amount of fuel by 1 gallon. This simulates putting fuel in the car. (The car can hold a maximum of 15 gallons.)
To be able to decrement the amount of fuel by 1 gallon, if the amount of fuel is greater than 0 gallons. This simulates burning fuel as the car runs.
The
OdometerClass: This class will simulate the car’s odometer. Its responsibilities are:To know the car’s current mileage.
To report the car’s current mileage.
To be able to increment the current mileage by 1 mile. The maximum mileage the odometer can store is 999,999 miles. When this amount is exceeded, the odometer resets the current mileage to 0.
To be able to work with a
FuelGaugeobject. It should decrease theFuelGaugeobject’s current amount of fuel by 1 gallon for every 24 miles traveled. (The car’s fuel economy is 24 miles per gallon.)
Demonstrate the classes by creating instances of each. Simulate filling the car up with fuel, then run a loop that increments the odometer until the car runs out of fuel. During each loop iteration, print the car’s current mileage and amount of fuel.