String Manipulation

You have been hired as a contract programmer by the Smithfield Natural Gas company.
Your first job is to write a program that prints a form letter to customers with an overdue
account. The letter should have the form shown in Figure . When the letter is printed by
your program, however, the fields shown in brackets will be replaced by actual values.

Figure
Dear :

Our records show that your account has a balance of $ and
a past-due amount of $. Your last payment was on .
Since we haven't heard from you in some time, would you please take
a moment to send us a check for the past-due amount? We value your
business and look forward to serving you in the future.
Sincerely,

The Management P.S. If you've already sent your payment, ignore this reminder.
Inside the letter, the fields listed in Table are shown in brackets.

Table
Field Description
Salutation (either Mr. or Ms.)
Salutation
The customer’s last name
Last-Name
The customer’s total account balance
Balance
The amount the account is past due
Past-Due
The date the customer last made a payment
Date




String Manipulation
Before the letter is printed, your program should ask the user to enter values for the fields
listed in Table . The values should then be inserted into the form letter as it is being
printed. The program should perform word-wrap, which means the sentences should be
adjusted so no word is split between two lines. Additionally, the letter should have -
character left and right margins.

Variables
Table lists the major variables needed.
Table
Variable Description
Eight constant global character arrays that hold portions of the form
part...part
letter A character array to hold the salutation
salutation
A character array to hold the customer’s last name
lastName
A character array to hold the date of the last payment
lastPayment
A character array to hold the text representation of the account
balance
balance
A character array to hold the text representation of the past due
pastDue
amount
A character to hold the user’s Y or N response when asked if they wish
again
to print another letter
An integer that holds the printing position. Each time a character is
position
printed, this variable is incremented. It is used to determine when the
end of the line is near
Global Variables
The program uses eight constant global character arrays to hold the sections of the form
letter that are always the same. The arrays, named
through
, are defined and
Part
Part
initialized as follows:
const char part[] = "Dear ";
const char part[] = "Our records show that your account has a"
" balance of $";
const char part[] = " and a past due amount of $";
const char part[] = "Your last payment was on ";
const char part[] = "Since we haven't heard from you in some"
" time, would you please take a moment to send"
" us a check for the past due amount? We value"
" your business and look forward to serving you"
" in the future.\n\n";
const char part[] = "Sincerely,\n";
const char part[] = "The Management\n\n";
const char part[] = "P.S. If you've already sent your payment, ignore"
" this reminder.";



String Manipulation
NOTE:
Notice that some of the arrays are initialized with what appears to be more than
one string. For instance, look at the initialization of
:
part
const char part[] = "Our records show that your account has a"
" balance of $";
The two strings are actually concatenated into one string. This allows the pogrammer to
easily span multiple lines when initializing character arrays with long strings. The same
technique is used with
and
.
part
part
Modules
The program will consist of the functions listed in Table .
Table
Function Description
The program’s
function. Calls the
and
main
main
getInfo
printLetter
functions.
Calls the
function to get the salutation. Then asks the user to enter
getInfo
getSal
the customer’s last name, account balance, past-due amount, and date of
last payment.
Prints a menu allowing the user to select either Mr. or Ms. as the
getSal
salutation.
Controls the printing of the form letter once the fields have been input by
printLetter
the user. Calls the
function.
printLine
Prints a line of text starting at the current printing position. This function
printLine
performs word-wrap when near the end of the line. It keeps the
position
variable updated as well.
Function
main
Function
contains the array definitions for the salutation, last name, date of last pay-
main
ment, account balance, and past due amount. A
loop calls the
and
do-while
getInfo
functions. The loop repeats as long as the user wishes to print form letters.
printLetter
Here is the pseudocode:
Do
Call getInfo to get the salutation, last name, balance, past-due
amount, and date of last payment from the user.
Call printLetter to print the form letter.
Ask the user if another letter is to be printed.
While the user wants to print another letter.



String Manipulation
Here is the function’s actual C++ code:
int main()
{
char salutation[]; // To hold the salutation
char lastName[]; // Customer's last name
char lastPayment[]; // Date of last payment
char balance[]; // Account balance
char pastDue[]; // Amount past due
char again; // To hold Y or N
do
{
// Call getInfo to get input from the user
getInfo(salutation, lastName, balance, pastDue,
lastPayment);
cout << "\n\n";
// Now print the form letter
printLetter(salutation, lastName, balance, pastDue,
lastPayment);
cout << "\n\nDo another letter? (Y/N) ";
cin >> again;
} while (toupper(again) == 'Y');
return ;
}
Notice that pointers to
,
,
,
, and
salutation
lastName
balance
pastDue
lastPayment
are passed to
and
. When
returns, these fields will have
getInfo
printLetter
getInfo
the values provided by the user stored in them.
will retrieve the values and
printLetter
insert them in the form letter.
The
getInfo
Function
This function first calls the
function (to get the salutation), and then asks the user
getSal
to enter the customer’s last name, account balance, past due amount, and the date of the
last payment. These values are then stored in the arrays whose addresses are passed into
the function as arguments. Here is the pseudocode:
Call getSal.
Ask the user to enter the customer’s last name.
Convert the first character of the last name to upper case.
Ask the user to enter the customer’s account balance.
Ask the user to enter the account’s past due amount.
Ask the user to enter the date of the last payment.
Notice that after the user enters the customer’s last name, the function automatically con-
verts its first character to upper case. This is in case the user entered the name in all lower
case. Here is the function’s C++ code:
void getInfo(char *sal, char *lname, char *bal, char *due,
char *lastPay)
{
getSal(sal);
cout << "Last name: ";
cin >> lname;



String Manipulation
lname[] = toupper(lname[]);
cout << "Account balance: ";
cin >> bal;
cout << "Past due amount: ";
cin >> due;
cout << "Date of last payment(MM/DD/YYYY): ";
cin >> lastPay;
}
The
getSal
Function
This function displays a menu allowing the user to select a salutation from either Mr. or
Ms. The choice is then stored in the array whose address is passed into the function as an
argument. Here is the pseudocode:
Do
Display menu with choice being Mr. and choice being Ms.
Ask user to select a salutation.
While the user does not select or from the menu.
If the user selected
The salutation is Mr.
else
The salutation is Ms.
End If.
Here is the function’s C++ code:
void getSal(char *sal)
{
int choice;
do
{
cout << "Salutation:\n";
cout << "\t) Mr.\n";
cout << "\t) Ms.\n";
cout << "Select one: ";
cin >> choice;
} while (choice != && choice != );
if (choice == )
strcpy(sal, "Mr.");
else
strcpy(sal, "Ms.");
}
The
printLetter
Function
Once the user has entered values for all the fields, this function controls the printing of the
letter. It has one local variable,
, which is an integer. This variable keeps track of
position
the number of characters printed on the current line. This is crucial information for the
function, which performs word-wrap. Below is the function’s pseudocode. (It
printLine
might help you to refer to the contents of the global arrays
through
as you
Part
Part
read the code.)



String Manipulation
// First print the salutation part of the letter.
Set the position variable to (for printLine).
Call printLine to print the part array.
Print the salutation, followed by a space, followed by the
customer’s last name, followed by a colon.
// Next print the body of the letter.
Set the position variable to zero.
Call printLine to print the part array.
Print the customer’s balance.
Adjust the position variable.
Call printLine to print the part array.
Print the past-due amount.
Adjust the position variable.
Call printLine to print the part array.
Print the date of the last payment.
Adjust the position variable.
Call printLine to print the part array.
// Next print the letter’s closing.
Set the position variable to zero (to start a new line.)
Call printLine to print the part array.
Set the position variable to zero (to start a new line).
Call printLine to print the part array.
// Last, print the PS reminder.
Set the position variable to zero (to start a new line).
Call printLine to print the part array.
The
function updates the
variable. When
prints one
printLine
position
PrintLetter
of the fields, such as balance, it must adjust the position variable. This is so the
function will accurately detect the end of each line. Notice that every time a
printLine
new line is to be started,
is reset to zero. Here is the C++ code for the function:
position
void printLetter(char *sal, char *lname, char *bal, char *due,
char *lastPay)
{
int position;
// Print the salutation part of the letter
position = ; // Start a new line
printLine(part, position);
cout << sal << " " << lname << ":" << endl << endl;
// Print the body of the letter
position = ; // Start a new line.
printLine(part, position);
cout << bal; // Print account balance.
// Add length of balance to position.
position += strlen(bal);
printLine(part, position);
cout << due << ". "; // Print past due amount
position += strlen(due)+ ;
// Add length of due and the period and space at the
// end of the sentence to position.
printLine(part, position);
cout << lastPay << ". "; // Print date of last payment.



String Manipulation
// Now Add length of lastPay and the period and space at the
// end of the sentence to position.
position += strlen(lastPay) + ;
printLine(part, position);
// Print the closing
position = ; // Start a new line
printLine(part, position);
position = ; // Start a new line
printLine(part, position);
// Print the PS reminder
position = ; // Start a new line
printLine(part, position);
}
The printLine Function
This function prints each individual line of the letter. It takes two arguments: the address
of the string that is to be printed on the line, and the variable used to store the number of
characters printed (the
variable). The number of characters printed on the line
position
is important because the program must perform word-wrap. This happens when the word
being printed at the end of a line will not entirely fit. Instead of printing part of the word
on one line and then continuing it on the next line, the program is to start the word on the
next line. Here is the function’s pseudocode:
If the line is at or past the right margin
Start a new line.
End If.
While not at the end of the string
If or more characters have been printed AND the next
character is a space
Perform word-wrap.
End If.
If at the beginning of a new line
Print the left margin ( spaces).
Add to the number of characters printed.
End If.
Print the next character.
Add one to the number of characters printed.
End While.
The first
statement simply checks to see if the current printing position is at or beyond
if
the right margin. Because the letter has ten-character margins, any position beyond the
th character is in the right margin.
Inside the
loop another
statement checks to see if or more characters have
while
if
been printed. This is the part that controls word-wrap. The function begins watching for a
space separating words at the th character. If a break between two words appears any-
where after the th character, a new line is started. The next
statement checks to see
if
a new line has begun. If so, it prints the spaces that make the left margin. After all
if
this has taken place, the next character is printed and the character count is incremented.



String Manipulation
Here is the C++ code for the function:
void printLine(const char *line, int &startCount)
{
int charCount = ;
if (startCount >= ) // If the line is already at
{ // or past the right margin...
cout << "\n"; // Start a new line.
startCount = ; // Reset startCount.
}
// The following while loop cycles through the string
// printing it one character at a time. It watches for
// spaces after the th position so word-wrap may be
// performed.
while (line[charCount] != '\')
{
if (startCount >= && line[charCount] == ' ')
{
cout << " \n"; // Print right margin.
charCount++; // Skip over the space
startCount = ;
}
if (startCount == )
{
cout << " "; // Print left margin.
startCount = ;
}
cout.put(line[charCount]); // Print the character.
charCount++; // Update subscript.
startCount++; // Update position counter.
}
}
NOTE:
The
parameter is a reference to the
variable in the
startCount
position
function.
printLetter
The Entire Program
Program - shows the entire program’s source code.



String Manipulation
Program -

// This program prints a simple form letter reminding a customer

// of an overdue account balance.

#include

#include

#include

using namespace std;


// Function Prototypes

void printLetter(char *, char *, char *, char *, char *);

void getInfo(char *, char *, char *, char *, char *);

void getSal(char *);

void printline(const char *, int&);


// Strings that make up the form letter

const char part[] = "Dear ";

const char part[] = "Our records show that your account has a"

" balance of $";

const char part[] = " and a past due amount of $";

const char part[] = "Your last payment was on ";

const char part[] = "Since we haven't heard from you in some"

" time, would you please take a moment to send"

" us a check for the past due amount? We value"

" your business and look forward to serving you"

" in the future.\n\n";

const char part[] = "Sincerely,\n";

const char part[] = "The Management\n\n";

const char part[] = "P.S. If you've already sent your payment, ignore"

" this reminder.";


int main()

{

char salutation[]; // To hold the salutation

char lastName[]; // Customer's last name

char lastPayment[]; // Date of last payment

char balance[]; // Account balance

char pastDue[]; // Amount past due

char again; // To hold Y or N


do

{

// Call getInfo to get input from the user

getInfo(salutation, lastName, balance, pastDue,

lastPayment);

cout << "\n\n";

// Now print the form letter

printLetter(salutation, lastName, balance, pastDue,

lastPayment);

cout << "\n\nDo another letter? (Y/N) ";

cin >> again;

} while (toupper(again) == 'Y');

return ;

}

(program continues)



String Manipulation
Program -
(continued)

//*****************************************************************

// Definition of function getInfo. *

// This function allows the user to enter the following items: *

// salutation, last name, account balance, past due amount, and *

// date of last payment. The function arguments are pointers to *

// strings where the input will be stored. *

//*****************************************************************


void getInfo(char *sal, char *lname, char *bal, char *due,

char *lastPay)

{

getSal(sal);

cout << "Last Name: ";

cin >> lname;

lname[] = toupper(lname[]);

cout << "Account balance: ";

cin >> bal;

cout << "Past due Amount: ";

cin >> due;

cout << "Date of last payment (MM/DD/YYYY): ";

cin >> lastPay;

}


//****************************************************************

// Definition of function getSal. *

// This function gives the user a menu from which to pick a *

// suitable title for the letter's addressee. The choices are *

// Mr. and Ms. The choice will be copied to the address pointed *

// to by sal. *

//****************************************************************


void getSal(char *sal)

{

int choice;


do

{

cout << "salutation:\n";

cout << "\t) Mr.\n";

cout << "\t) Ms.\n";

cout << "Select one: ";

cin >> choice;

} while (choice != && choice != );


if (choice == )

strcpy(sal, "Mr.");

else

strcpy(sal, "Ms.");

}

(program continues)



String Manipulation
Program -
(continued)

//*************************************************************

// Definition of function printLetter. *

// This function prints the form letter. The parameters are *

// pointers to the fields that contain user input. *

//*************************************************************


void printLetter(char *sal, char *lname, char *bal, char *due,

char *lastPay)

{

int position;


// Print the salutation part of the letter

position = ; // Start a new line.

printline(part, position);

cout << sal << " " << lname << ":" << endl << endl;


// Print the body of the letter

position = ; // Start a new line.

printline(part, position);

cout << bal; // Print account balance.


// Add length of balance to position.

position += strlen(bal);

printline(part, position);

cout << due << ". "; // Print past due amount

position += strlen(due)+ ;


// Add length of due and the period and space at the

// end of the sentence to position.

printline(part, position);

cout << lastPay << ". "; // Print date of last payment.


// Now Add length of lastPay and the period and space at the

// end of the sentence to position.

position += strlen(lastPay) + ;

printline(part, position);


// Print the closing.

position = ; // Start a new line.

printline(part, position);

position = ; // Start a new line.

printline(part, position);


// Print the PS reminder.

position = ; // Start a new line.

printline(part, position);

}

(program continues)



String Manipulation
Program -
(continued)

//*******************************************************************

// Definition of function printline. *

// This function has two parameters: line and startCount. *

// The string pointed to by line is printed. startCount is the *

// starting position of the line in an character field. There *

// are -character left and right margins within the *

// character field. The function performs word-wrap by looking *

// for space character within the line at or after the th *

// character. A new line is started when a space is found, or the *

// end of the field is reached. *

//*******************************************************************


void printline(const char *line, int &startCount)

{

int charCount = ;


if (startCount >= ) // If the line is already at

{ // or past the right margin...

cout << "\n"; // Sart a new line.

startCount = ; // Reset startCount.

}


// The following while loop cycles through the string

// printing it one character at a time. It watches for

// spaces after the th position so word-wrap may be

// performed.

while (line[charCount] != '\')

{

if (startCount >= && line[charCount] == ' ')

{

cout << " \n"; // Print right margin.

charCount++; // Skip over the space.

startCount = ;

}

if (startCount == )

{

cout << " "; // Print left margin.

startCount = ;

}

cout.put(line[charCount]); // Print the character.

charCount++; // Update subscript.

startCount++; // Update position counter.

}

}



String Manipulation
Program -
(continued)
Program Output with Example Input Shown in Bold
Salutation:
) Mr.
) Ms.
Select one:
[Enter]
Last name:
Jones [Enter]
Account balance:
. [Enter]
Past due amount:
. [Enter]
Date of last payment(MM/DD/YYYY):
// [Enter]
Dear Mr. Jones:
Our records show that your account has a balance of
$. and a past due amount of $.. Your last
payment was on //. Since we haven't heard from
you in some time, would you please take a moment to
send us a check for the past-due amount? We value your business
and look forward to serving you in the future.
Sincerely,
The Management
P.S. If you've already sent your payment, ignore this
reminder.
Do another letter? (Y/N)
y [Enter]
Salutation:
) Mr.
) Ms.
Select one:
[Enter]
Last name:
Hildebrand [Enter]
Account balance:
,. [Enter]
Past due amount:
,. [Enter]
Date of last payment(MM/DD/YYYY):
// [Enter]
Dear Ms. Hildebrand:
Our records show that your account has a balance of
$,. and a past due amount of $,.. Your
last payment was on //. Since we haven't heard
from you in some time, would you please take a moment
to send us a check for the past-due amount? We value
your business and look forward to serving you in the
future.
Sincerely,
The Management
P.S. If you've already sent your payment, ignore this
reminder.
Do another letter? (Y/N)
n [Enter]

String Class Implementation


Earlier in this book you were introduced to the C++ standard library
class. The string class automatically handles many of the tedious tasks involved in using strings,
string such as dynamic memory allocation, and bounds checking. It also overloads operators,
such as and , and offers many member functions that ease the job of working with + =
strings. In this section, however, you will learn to write your own string handling class. In
the process, you will see examples of the copy constructor and overloaded operators in
full action.

The MyString Class
The class defined in this section is an abstract data type for handling strings. It
MyString offers several advantages over standard C++ character array manipulation:

• Memory is dynamically allocated for any string stored in a object. The MyString programmer using this class doesn’t need to be concerned with how large to make
an array.
• Strings may be assigned to a object with the = operator. The program-
MyString mer using this class does not have to call the function.
strcpy
• One string may be concatenated to another with the += operator. This eliminates the need for the function.
strcat
• Strings may be tested with the relational operators. The programmer using this class doesn’t have to call the function.

strcmp

The following program listings show the class implementation.
Contents of
MyString.h

// Specification file for the MyString class

#ifndef MYSTRING_H

#define MYSTRING_H

#include

using namespace std;




Creating a String Class

class MyString; // Forward declaration.

ostream &operator<<(ostream &, const MyString &);

istream &operator>>(istream &, MyString &);


// MyString class. An abstract data type for handling strings.


class MyString

{

private:

char *str;

int len;

public:

// Default constructor

MyString()

{ str = NULL; len = ; }


// Copy constructor

MyString(MyString &right)

{ str = new char[right.length() + ];

strcpy(str, right.getValue());

len = right.length(); }


// The following constructor initializes the

// MyString object with a C-string

MyString(char *sptr)

{ len = strlen(sptr);

str = new char[len + ];

strcpy(str, sptr); }


// Destructor

~MyString()

{ if (len != ) delete [] str; }


// The length function returns the string length.

int length() const

{ return len; }


// The getValue function returns the string.

const char *getValue() const

{ return str; };


// Overloaded operators

const MyString operator+=(MyString &);

const char *operator+=(const char *);

const MyString operator=(MyString &);

const char *operator=(const char *);

int operator==(MyString &);

int operator==(const char *);

int operator!=(MyString &);

int operator!=(const char *);

bool operator>(MyString &);

bool operator>(const char *);

bool operator<(MyString &);


Creating a String Class

bool operator<(const char *);

bool operator>=(MyString &);

bool operator>=(const char*);

bool operator<=(MyString &);

bool operator<=(const char *);


// Friends

friend ostream &operator<<(ostream &, const MyString &);

friend istream &operator>>(istream &, MyString &);

};


#endif
Contents of
MyString.cpp

// Implementation file for the MyString class

#include // For string library functions

#include "MyString.h"

using namespace std;


//*************************************************

// Overloaded = operator. Called when operand *

// on the right is another MyString object. *

// Returns the calling object. *

//*************************************************


const MyString MyString::operator=(MyString &right)

{

if (len != )

delete [] str;

str = new char[right.length() + ];

strcpy(str, right.getValue());

len = right.length();

return *this;

}


//*************************************************

// Overloaded = operator. Called when operand *

// on the right is a C-string. *

// Returns the str member of the calling object. *

//*************************************************


const char *MyString::operator=(const char *right)

{

if (len != )

delete [] str;

len = strlen(right);

str = new char[len + ];

strcpy(str, right);

return str;

}



Creating a String Class

//*************************************************

// Overloaded += operator. Called when operand *

// on the right is another MyString object. *

// Concatenates the str member of right to the *

// str member of the calling object. *

// Returns the calling object. *

//*************************************************


const MyString MyString::operator+=(MyString &right)

{

char *temp = str;


str = new char[strlen(str) + right.length() + ];

strcpy(str, temp);

strcat(str, right.getValue());

if (len != )

delete [] temp;

len = strlen(str);

return *this;

}


//*************************************************

// Overloaded += operator. Called when operand *

// on the right is a string. Concatenates the *

// str member of right to the str member of *

// the calling object. *

// Returns the str member of the calling object. *

//*************************************************


const char *MyString::operator+=(const char *right)

{

char *temp = str;


str = new char[strlen(str) + strlen(right) + ];

strcpy(str, temp);

strcat(str, right);

if (len != )

delete [] temp;

return str;

}


//*****************************************************

// Overloaded == operator. *

// Called when the operand on the right is a MyString *

// object. Returns if right.str is the same as str. *

//*****************************************************


int MyString::operator==(MyString &right)

{

return !strcmp(str, right.getValue());

}



Creating a String Class

//****************************************************

// Overloaded == operator. *

// Called when the operand on the right is a string. *

// Returns if right is the same as str. *

//****************************************************


int MyString::operator==(const char *right)

{

return !strcmp(str, right);

}


//*********************************************************

// Overloaded != operator. *

// Called when the operand on the right is a MyString *

// object. Returns true if right.str is not equal to str. *

//*********************************************************


int MyString::operator!=(MyString &right)

{

return strcmp(str, right.getValue());

}


//****************************************************

// Overloaded != operator. *

// Called when the operand on the right is a string. *

// Returns true if right is not equal to str. *

//****************************************************


int MyString::operator!=(const char *right)

{

return strcmp(str, right);

}


//*********************************************************

// Overloaded > operator. *

// Called when the operand on the right is a MyString *

// object. Returns true if str is greater than right.str. *

//*********************************************************


bool MyString::operator>(MyString &right)

{

bool status;


if (strcmp(str, right.getValue()) > )

status = true;

else

status = false;

return status;

}


//****************************************************

// Overloaded > operator. *

// Called when the operand on the right is a string. *

// Returns true if str is greater than right. *

//****************************************************



Creating a String Class

bool MyString::operator>(const char *right)

{

bool status;


if (strcmp(str, right) > )

status = true;

else

status = false;

return status;

}


//******************************************************

// Overloaded < operator. *

// Called when the operand on the right is a MyString *

// object. Returns true if str is less than right.str. *

//******************************************************


bool MyString::operator<(MyString &right)

{

bool status;


if (strcmp(str, right.getValue()) < )

status = true;

else

status = false;

return status;

}


//****************************************************

// Overloaded < operator. *

// Called when the operand on the right is a string. *

// Returns true if str is less than right. *

//****************************************************


bool MyString::operator<(const char *right)

{

bool status;


if (strcmp(str, right) < )

status = true;

else

status = false;

return status;

}


//*****************************************************

// Overloaded >= operator. *

// Called when the operand on the right is a MyString *

// object. Returns true if str is greater than or *

// equal to right.str *

//*****************************************************


bool MyString::operator>=(MyString &right)

{

bool status;



Creating a String Class

if (strcmp(str, right.getValue()) >= )

status = true;

else

status = false;

return status;

}


//*********************************************************

// Overloaded >= operator. *

// Called when the operand on the right is a string. *

// Returns true if str is greater than or equal to right. *

//*********************************************************


bool MyString::operator>=(const char *right)

{

bool status;


if (strcmp(str, right) >= )

status = true;

else

status = false;

return status;

}


//**********************************************************

// Overloaded <= operator. *

// Called when the operand on the right is a MyString *

// object. Returns true if right.str is less than or equal *

// to right.str. *

//**********************************************************


bool MyString::operator<=(MyString &right)

{

bool status;


if (strcmp(str, right.getValue()) <= )

status = true;

else

status = false;

return status;

}


//******************************************************

// Overloaded <= operator. *

// Called when the operand on the right is a string. *

// Returns true if str is less than or equal to right. *

//******************************************************


bool MyString::operator<=(const char *right)

{

bool status;


if (strcmp(str, right) <= )

status = true;

else

status = false;


Creating a String Class

return status;

}


//*************************************************

// Overloaded stream insertion operator (<<). *

//*************************************************


ostream &operator<<(ostream &strm, const MyString &obj)

{

strm << obj.str;

return strm;

}


//*************************************************

// Overloaded stream extraction operator (>>). *

//*************************************************


istream &operator>>(istream &strm, MyString &obj)

{

strm.getline(obj.str, obj.len);

strm.ignore();

return strm;

}
The Copy Constructor
Because the
class has a pointer as a member and dynamically allocates memory
MyString
to store its string value, a copy constructor is provided. This function will cause the object
to properly set up its data when initialized with another
object.
MyString
The Overloaded = Operators
The
class has two overloaded = operators. The first is for assigning one
MyString
object to another. This operator function is called when the operand on the
MyString
right of the = sign is a
object, as shown in the following code segment:
MyString
MyString first("Hello"), second;
second = first;
The second version of
’s = operator is for assigning a traditional C-string to a
MyString
object. This operator function is called when the operand on the right of = is a
MyString
string literal or any pointer to a C-string (such as the name of a char array). This is shown
in the following program segment:
MyString name;
char who[] = "Jimmy";
name = who;
The Overloaded += Operators
The += operator is designed to concatenate the string on its right to the
object
MyString
on its left. Like the = operators,
has two versions of +=. The first version is
MyString
designed to work when the right operand is another
object, as shown in the fol-
MyString
lowing program segment:


Creating a String Class
MyString first("Hello "), second("world");
first += second;
The second version of the += operator will be called when the right operand is a literal
string or any pointer to a character. It is shown here:
MyString first("Hello ");
first += "World";
The Overloaded == Operators
The
object has overloaded versions of the == operator for performing equality
MyString
tests. Like the other operators, the first version is designed to work with another
object and the second is designed to work with a traditional C-string.
MyString
The
operator functions return an integer that can be treated as a Boolean value. Both
==
functions use
to compare the operands, and then returns the negative of
’s
strcmp
strcmp
return value. (Recall that
uses inverted logic: It returns when its arguments are
strcmp
equal, and returns a nonzero value when they are not equal.) So, these operator functions
return
if the string contained in the right operand matches the
member of the
true
str
calling object. If the strings of the two operands do not match, the functions return
.
false
These operator functions allow the programmer using this class to construct relational
expressions such as those shown in the following program segments:
MyString name("John"), name("John");
if (name == name)
cout << "The names are the same.\n";
else
cout << "The names are different.\n";
MyString name("John");
if (name == "Jon")
cout << "The names are the same.\n";
else
cout << "The names are different.\n";
The Overloaded > and < Operators
The
object has two overloaded versions of the
operator for performing
MyString
>
greater-than tests, and the < operator for performing less-than tests. The first version of
each is designed to work with another
object and the second is designed to
MyString
work with a traditional C-string. (The functions use the library function
to deter-
strcmp
mine if a greater-than or less-than relationship exists.)
The > functions return a
if the
member of the calling object is greater than the
true
str
string contained in the right operand. Otherwise, the functions return
. The < func-
false
tions return a
if the
member of the calling object is less than the string contained
true
str
in the right operand. Otherwise, they return
.
false
These operator functions allow the programmer using this class to construct relational
expressions such as those shown in the following program segments:
MyString name("John"), name("Jon");
if (name > name)


Creating a String Class
cout << "John is greater than Jon.\n";
else
cout << "John is not greater than Jon.\n";
MyString name("John");
if (name < "Jon")
cout << "John is less than Jon.\n";
else
cout << "John is not greater than Jon.\n";
The Overloaded >= and <= Operators
The
object has two overloaded versions of the >= operator for performing
MyString
greater-than or equal-to tests, and the <= operator for performing less-than or equal-to
tests. The first version of each is designed to work with another
object and the
MyString
second is designed to work with a traditional C-string. (The functions use the library func-
tion
to determine if a greater-than or less-than relationship exists.)
strcmp
The >= functions return a
if the
member of the calling object is greater than or
true
str
equal to the string contained in the right operand. Otherwise, the functions return
.
false
The <= functions return
if the
member of the calling object is less than or equal
true
str
to the string contained in the right operand. Otherwise, they return
.
false
These operator functions allow the programmer using this class to construct relational
expressions such as those shown in the following program segments:
MyString name("John"), name("Jon");
if (name >= name)
cout << "John is greater than or equal to Jon.\n";
else
cout << "John is not greater than or equal to Jon.\n";
MyString name("John");
if (name <= "Jon")
cout << "John is less than or equal to Jon.\n";
else
cout << "John is not less than or equal to Jon.\n";
Program - shows how
’s += operator performs string concatenation. Addi-
MyString
tionally, the program’s source code demonstrates how
allows the programmer
MyString
to treat strings much like any other built-in data type.
Program -

// This program demonstrates the MyString class.

#include

#include "MyString.h"


int main()

{

// Define and initialize several MyString objects.

MyString object("This"), object("is");

MyString object("a test.");

MyString object = object;

MyString object("is only a test.");
(program continues)


Creating a String Class
Program -
(continued)

// Define a C-string.

char string[] = "a test.";


// Display the MyString objects.

cout << "object: " << object << endl;

cout << "object: " << object << endl;

cout << "object: " << object << endl;

cout << "object: " << object << endl;

cout << "object: " << object << endl;


// Display the C-string.

cout << "string: " << string << endl;


// Test the overloaded += operator.

object += " ";

object += object;

object += " ";

object += object;

object += " ";

object += object;

object += " ";

object += object;

cout << "object: " << object << endl;


return ;

}
Program Output
object: This
object: is
object: a test.
object: This
object: is only a test.
string: a test.
object: This is a test. This is only a test.
Program - shows how
’s relational operators can be used to compare
MyString
strings with the same ease that numeric data types are compared.
Program -

// This program demonstrates the MyString class.

#include

#include "MyString.h"

using namespace std;


int main()

{

// Define several MyString objects.

MyString name("Billy"), name("Sue");

MyString name("joe");

MyString string("ABC"), string("DEF");

(program continues)

Creating a String Class
Program -
(continued)

// Display the MyString object values.

cout << "name: " << name.getValue() << endl;

cout << "name: " << name.getValue() << endl;

cout << "name: " << name.getValue() << endl;

cout << "string: " << string.getValue() << endl;

cout << "string: " << string.getValue() << endl;


// Test the overloaded relational operators.

if (name == name)

cout << "name is equal to name.\n";

else

cout << "name is not equal to name.\n";


if (name == "joe")

cout << "name is equal to joe.\n";

else

cout << "name is not equal to joe.\n";


if (string > string)

cout << "string is greater than string.\n";

else

cout << "string is not greater than string.\n";


if (string < string)

cout << "string is less than string.\n";

else

cout << "string is not less than string.\n";


if (string >= string)

cout << "string is greater than or equal to string.\n";

else

cout << "string is not greater than or equal to string.\n";


if (string >= "ABC")

cout << "string is greater than or equal to ABC.\n";

else

cout << "string is not greater than or equal to ABC.\n";


if (string <= string)

cout << "string is less than or equal to string.\n";

else

cout << "string is not less than or equal to string.\n";


if (string <= "DEF")

cout << "string is less than or equal to DEF.\n";

else

cout << "string is not less than or equal to DEF.\n";


return ;

}


Creating a String Class
Program -
(continued)
Program Output
name: Billy
name: Sue
name: joe
string: ABC
string: DEF
name is not equal to name.
name is equal to joe.
string is not greater than string.
string is less than string.
string is not greater than or equal to string.
string is greater than or equal to ABC.
string is less than or equal to string.
string is less than or equal to DEF.


Namespaces

Introduction
Namespaces are an ANSI C++ feature that allows programmers to create a scope for global
identifiers. They are useful in preventing errors when two or more global declarations
use the same name.
For example, assume you are a programmer in the accounting department of a business.
Your company has purchased two libraries of C++ code from a software vendor. One of
the libraries is designed to handle customer accounts, and contains a class object named
payable
. The other library is designed to handle company payroll, and also has a class
object named
payable
. You are writing a program that integrates both libraries, but the
compiler generates an error because the two class objects have the same name. You cannot
modify the libraries because the software vendor does not sell the source code, only libraries
of object code.
This problem can be solved when the software vendor places each library in its own
namespace. Each namespace has its own name, which must be used to qualify the name of
its members. For instance, the
payable
object that is part of the customer accounts library
might exist in a namespace named
customer
, while the object that is part of the employee
payroll library might exist in a namespace named
payroll
. When you, the application
programmer, work with the objects, you must specify the namespace that the object is a
member of. One way of accomplishing this is by extending the name of the object with the
namespace name, using the scope resolution operator. For example, the
payable
object
that is a member of the
customer
namespace is specified as
customer::payable
, and the
object that is a member of the
payroll
namespace is specified as
payroll::payable
.
Another way to specify the namespace is by placing a
using namespace
statement in the
source file that references the namespace’s member object. For example, the following
statement (placed near the beginning of a source file) instructs the compiler that the file
uses members of the
customer
namespace.
using namespace customer;
2
Appendix F: Namespaces
Likewise, the following statement instructs the compiler that the source file uses members
of the
payroll
namespace:
using namespace payroll;
When a
using namespace
statement has been placed in a source file, it is no longer necessary
for statements in that source file to qualify the names of the namespace’s members
with the namespace name and the scope resolution operator.
Defining a Namespace
A namespace is defined in the following manner.
For example, look at Program F-1. It defines the
test
namespace, which has three members:
x
,
y
, and
z
.
In Program F-1, the variables
x
,
y
, and
z
are defined in the
test
namespace’s scope. Each
time the program accesses one of these variables,
test::
must precede the variable name.
Otherwise, a compiler error will occur.
namespace
namespace_name
{
declarations…
}
Program F-1
1 // Demonstrates a simple namespace
2 #include
3 using namespace std;
4
5 namespace test
6 {
7 int x, y, z;
8 }
9
10 int main()
11 {
12 test::x = 10;
13 test::y = 20;
14 test::z = 30;
15 cout << "The values are:\n";
16 cout << test::x << " " << test::y
17 << " " << test::z << endl;
18 return 0;
19 }
Program Output
The values are:
10 20 30
Appendix F: Namespaces
3
Program F-2 demonstrates how programmers can use namespaces to resolve naming
conflicts. The program defines two namespaces,
test1
and
test2
. Both namespaces have
variables named
x
,
y
, and
z
as members.
An alternative approach to qualifying the names of namespace members is to use the
using
namespace
statement. This statement tells the compiler which namespace to search
for an identifier, when the identifier cannot be found in the current scope. Program F-3
demonstrates the statement.
Program F-2
1 // Demonstrates two namespaces
2 #include
3 using namespace std;
4
5 namespace test1
6 {
7 int x, y, z;
8 }
9
10 namespace test2
11 {
12 int x, y, z;
13 }
14
15 int main()
16 {
17 test1::x = 10;
18 test1::y = 20;
19 test1::z = 30;
20 cout << "The test1 values are:\n";
21 cout << test1::x << " " << test1::y
22 << " " << test1::z << endl;
23 test2::x = 1;
24 test2::y = 2;
25 test2::z = 3;
26 cout << "The test2 values are:\n";
27 cout << test2::x << " " << test2::y
28 << " " << test2::z << endl;
29 return 0;
30 }
Program Output
The test1 values are:
10 20 30
The test2 values are:
1 2 3
4
Appendix F: Namespaces
The
using namespace demo;
statement eliminates the need to precede
testObject
with
demo::
. The compiler automatically uses the namespace demo to find the identifier.
ANSI C++ and the
std
Namespace
All the identifiers in the ANSI standard header files are part of the
std
namespace. In
ANSI C++,
cin
and
cout
are written as
std::cin
and
std::cout
. If you do not wish to
specify
std::
with
cin
or
cout
(or any of the ANSI standard identifiers), you must write
the following statement in your program:
using namespace std;
Program F-3
Contents of
nsdemo.h
1 // This file defines a namespace named demo.
2 // In the demo namespace a class named NsDemo
3 // is declared, and an instance of the class
4 // named testObject is defined.
5
6 namespace demo
7 {
8 class NsDemo
9 {
10 public:
11 int x, y, z;
12 };
13
14 NsDemo testObject;
15 }
Contents of Main File,
PrF-3.cpp
1 // A demonstration of the using namespace statement.
2 #include
3 #include "nsdemo.h"
4 using namespace std;
5 using namespace demo;
6
7 int main()
8 {
9 testObject.x = 10;
10 testObject.y = 20;
11 testObject.z = 30;
12 cout << "The values are:\n"
13 << testObject.x << " "
14 << testObject.y << " "
15 << testObject.z << endl;
16 return 0;
17 }
Program Output
The values are:
10 20 30

The `fork()' system call


From the C programming language, the fork() system call is well known. When a program needs to start a new process, system() can be used, but this requires the program to wait for the child process to terminate. The more general way to spawn subprocesses is to call fork().

In this section we will see how C++ can be used to wrap classes around a complex system call like fork(). Much of what follows in this section directly applies to the Unix operating system, and the discussion will therefore focus on that operating system. However, other systems usually provide comparable facilities. The following discussion is based heavily on the notion of design patterns, as published by Gamma et al. (1995)

When fork() is called, the current program is duplicated in memory, thus creating a new process, and both processes continue their execution just below the fork() system call. The two processes may, however, inspect the return value of fork(): the return value in the original process (called the parent process) differs from the return value in the newly created process (called the child process):

  • In the parent process fork() returns the process ID of the child process created by the fork() system call. This is a positive integer value.
  • In the child process fork() returns 0.
  • If fork() fails, -1 is returned.

A basic Fork class should hide all bookkeeping details of a system call like fork() from its users. The class Fork developed here will do just that. The class itself only needs to take care of the proper execution of the fork() system call. Normally, fork() is called to start a child process, usually boiling down to the execution of a separate process. This child process may expect input at its standard input stream and/or may generate output to its standard output and/or standard error streams. Fork does not know all this, and does not have to know what the child process will do. However, Fork objects should be able to activate their child processes.

Unfortunately, Fork's constructor cannot know what actions its child process should perform. Similarly, it cannot know what actions the parent process should perform. For this particular situation, the template method design pattern was developed. According to Gamma c.s., the template method design pattern

``Define(s) the skeleton of an algorithm in an operation, deferring some steps to subclasses. (The) Template Method (design pattern) lets subclasses redefine certain steps of an algorithm, without changing the algorithm's structure.''

This design pattern allows us to define an abstract base class already implementing the essential steps related to the fork() system call and deferring the implementation of certain normally used parts of the fork() system call to subclasses.

The Fork abstract base class itself has the following characteristics:

  • It defines a data member d_pid. This data member will contain the child's process id (in the parent process) and the value 0 in the child process:

· class Fork

· {

· int d_pid;

  • Its public interface declares but two members:
    • a fork() member function, performing the actual forking (i.e., it will create the (new) child process);
    • an empty virtual destructor ~Fork(), which may be overridden by derived classes.

Here is Fork's complete public interface:

virtual ~Fork()

{}

void fork();

  • All remaining member functions are declared in the class's protected section and can thus only be used by derived classes. They are:
    • The member function pid(), allowing derived classes to access the system fork()'s return value:

o int pid()

o {

o return d_pid;

o }

    • A member int waitForChild(), which can be called by parent processes to wait for the completion of their child processes (as discussed below). This member is declared in the class interface. Its implementation is

o #include "fork.ih"

o

o int Fork::waitForChild()

o {

o int status;

o

o waitpid(d_pid, &status, 0);

o

o return WEXITSTATUS(status);

o }

This simple implementation returns the child's exit status to the parent. The called system function waitpid() blocks until the child terminates.

    • When fork() system calls are used, parent processes and child processes may always be distinguished. The main distinction between these processes is that d_pid will be equal to the child's process-id in the parent process, while d_pid will be equal to 0 in the child process itself. Since these two processes may always be distinguished, they must be implemented by classes derived from Fork. To enforce this requirement, the members childProcess(), defining the child process' actions and parentProcess(), defining the parent process' actions we defined as pure virtual functions:

o virtual void childProcess() = 0; // both must be implemented

o virtual void parentProcess() = 0;

    • In addition, communication between parent- and child processes may use standard streams or other facilities, like pipes (cf. section 20.3.3). To facilitate this inter-process communication, derived classes may implement:
      • childRedirections(): this member should be implemented if any standard stream (cin, cout) or cerr must be redirected in the child process (cf. section 20.3.1);
      • parentRedirections(): this member should be implemented if any standard stream (cin, cout) or cerr must be redirected in the parent process.

Redirection of the standard streams will be necessary if parent- and child processes should communicate with each other via the standard streams. Here are their default definitions provided by the class's interface:

virtual void childRedirections()

{}

virtual void parentRedirections()

{}

The member function fork() calls the system function fork() (Caution: since the system function fork() is called by a member function having the same name, the :: scope resolution operator must be used to prevent a recursive call of the member function itself). After calling ::fork(), depending on its return value, either parentProcess() or childProcess() is called. Maybe redirection is necessary. Fork::fork()'s implementation calls childRedirections() just before calling childProcess(), and parentRedirections() just before calling parentProcess():

#include "fork.ih"

void Fork::fork()

{

if ((d_pid = ::fork()) <>

throw "Fork::fork() failed";

if (d_pid == 0) // childprocess has pid == 0

{

childRedirections();

childProcess();

exit(1); // we shouldn't come here:

// childProcess() should exit

}

parentRedirections();

parentProcess();

}

In fork.cc the class's internal header file fork.ih is included. This header file takes care of the inclusion of the necessary system header files, as well as the inclusion of fork.h itself. Its implementation is:

#include "fork.h"

#include

#include

#include

#include

Child processes should not return: once they have completed their tasks, they should terminate. This happens automatically when the child process performs a call to a member of the exec...() family, but if the child itself remains active, then it must make sure that it terminates properly. A child process normally uses exit() to terminate itself, but it should be realized that exit() prevents the activation of destructors of objects defined at the same or more superficial nesting levels than the level at which exit() is called. Destructors of globally defined objects are activated when exit() is used. When using exit() to terminate childProcess(), it should either itself call a support member function defining all nested objects it needs, or it should define all its objects in a compound statement (e.g., using a throw block) calling exit() beyond the compound statement.

Parent processes should normally wait for their children to complete. The terminating child processes inform their parent that they are about to terminate by sending out a signal which should be caught by their parents. If child processes terminate and their parent processes do not catch those signal then such child processes remain visible as so-called zombie processes.

If parent processes must wait for their children to complete, they may call the member waitForChild(). This member returns the exit status of a child process to its parent.

There exists a situation where the child process continues to live, but the parent dies. In nature this happens all the time: parents tend to die before their children do. In our context (i.e. C++), this is called a daemon program: the parent process dies and the child program continues to run as a child of the basic init process. Again, when the child eventually dies a signal is sent to its `step-parent' init. No zombie is created here, as init catches the termination signals of all its (step-) children. The construction of a daemon process is very simple, given the availability of the class Fork (cf. section 20.3.2).