CS245 - Comp Sci II

Chapter 9 - Process II: Working With Classes

OBJECTIVES


9.1 Program in Progress: A Word Processor


Public
Document
Document(int)
~Document

InsertLine(int)
DeleteLine(int)
ChangeLine(int)
Display(int, int)
Save
Retrieve
Construct empty document
Construct empty document of specified size
Free up text storage

Add a line at specified position
Delete a line at specified position
Change line at specified position
Display lines in specified range
Store document to disk file
Read document from disk file

Private
maxLines
numberLines
ShiftLines(int, int)
Grow
text
int
int
Move lines of text from specified position up or down
Double size of text array
OCString*

9.2 Building Programs From Libraries

Streams, Revisited

		ios
	/	 | 	\
istream 	ostream fstreambase
|      \	/      \	| 
|       \  	/        \	|
ifstream    iostream 	     ofstream

istream.h

//extraction operators

istream& operator>> (int& i);

istream& operator>> (double& d);

istream& operator>> (char& c);

istream& operator>> (char* s);

istream& get(char& c);

istream& get(char* s, int n, int ch='n');

Stream cin is a predefined, open stream that is automatically associated with the standard input device.

Insertion operators and cout are defined similarily in ostream.h

File I/O uses ifstream and ofstream:

char fileName[30];
cout << "\nOpen which file?";
cin fileName;
ifstream fInput(fileName);
int i;
char c;
fInput >> i >> c;

Format-state flags

eof() - returns true if end-of-file condition detected on stream input

fail() - returns true if any attempt to do input or output fails

bad() - severe failure

good() - eof & fail & bad all false

ex. istream ist;

ist >> aValue;

while (!ist.fail())

{ //process aValue

...

}

while (!ist.fail()) can also be abbreviated by while (ist >> aValue)

Strings revisited

string.h functions:

Copy functions

char* strcpy(dest, src)
char* strncpy(dest, src, nbrChar)

Concatenation functions

char* strcat(dest, src)
char* strncat(dest, src, nbrChar)

Comparison functions

char* strcmp(src1, src2)
char* strncmp(src1, src2, nbrChar)

Search functions

char* strchr(src, ch) //returns pointer to first instance of ch in src
char* strrchr(src, ch) //returns pointer to last instance of ch in src
char* strstr(src1, src2) //returns pointer to first instance of src2 in src1

Length function

size_t strlen(src)

ex. p = strcat(u, strncpy(t, "bonehead", 4);

lexicographical order:

1. First n(>=0) characters of s and t are equal, but s[n+1]<t[n+1]

2. First n(>=0) characters of s and t match, but s has exactly n characters and t has more

// ­­­­­­­­­­ OCSTRING.H ­­­­­­­­­­
// This file contains the declaration of the Object Concept version of class OCString.

#ifndef _OCSTRING_H
#define _OCSTRING_H

#include <iostream.h> // for istream, ostream

class OCString
{
public:
// We define three different String constructors, and one destructor
	OCString(); // creates empty string
	OCString(char* array); // constructor using a standard C++ string
	OCString(const OCString& Other_String); // constructor using an OC String
	~OCString(); // destructor for our Strings
	int GetLength(); // returns current length

// We overload operators for assignment, subscripting, equality, extraction and insertion
	OCString& operator=(const OCString& str);
	char& operator[](int i);

	friend int operator==(const OCString& str1, const OCString& str2);
	friend istream& operator>>(istream& is, OCString& str);
	friend ostream& operator<<(ostream& os, OCString& str);
private:
	char* s; // pointer to the first character of the string
	int length; // current length of the string
};
#endif

9.3 Documenting Programs

1. An identifier should almost always be an English word that describes the purpose of the variable. (Except possibly for loop control variables)

2. Nouns should be used for class objects, types and quantities.

3. Verbs should be used to describe functions.

4. Names should be precise and unambiguous.

5. Be consistent with capitalization.

1. Include comments at the beginning of every file, at every declaration, at the beginning of every function, at every compound control structure, at any tricky or unusual code or anywhere theat requires explanation.

2. Comments should be in English and not be redundant.

3. Comments should relate parts of the program to one another.

4. Style of comments should be consistent.

// ­­­­­­­­­­ DOCUMENT.H ­­­­­­­­­­
// This file declares class Document, which serves as the basis for our PIP for chapter 9, // a line­oriented word processor.

#ifndef _DOCUMENT_H
#define _DOCUMENT_H
#include "OCSTRING.H" // for class OCString

class Document
{
public:
	Document(); // create default document of 10 lines
	Document(int i); // create document of specified size
	~Document(); // destructor frees document storage
	void InsertLine(int); // insert new line at specified position
	void DeleteLine(int); // delete line from specified position
	void ChangeLine(int); // change the line at specified position
	void Display(int, int); // display range of lines on screen
	void Save(); // save text to a file
	void Retrieve(); // retrieve text from a file
private:
	int maxLines; // current maximum for number of lines
	int numberLines; // current number of lines of text
	void ShiftLines(int, int); // shift lines up or down by one position
	void Grow(); // expand text array to fit more lines
	OCString* text; // points to the start of the text,
// so we can make it grow dynamically
};
#endif

9.4 Compiling Programs

syntax of a language = collection of grammar rules of a language

name ::= identifier | operator_function_name | conversion_function_name | qualified_name

qualified_name ::= class_name :: identifer | class_name :: operator_function_name | class_name :: conversion_function_name | class_name :: class_name | class_name :: ~class_name

conditional_expression ::= logical_OR_expression | logical_OR_expression ? expression : conditional_expression

assignment_expression ::= conditional_expression | unary_expression assignment_operator assignment_expression

assignment_operator ::= = | *= | /= | += | -= | %= | <<= | >>= | &= | ^= | |=

source code = data

|

compiler (translator)

|

object code

syntax error - C++ code violates one of the syntax rules (grammar)

RULES FOR REPAIRING SYNTAX ERRORS

1. Look at the line of code where the error is indicated.

2. If the source of the error is not evident, look earlier in the file of references to each of the identifiers that occur in the indicated line.

3. Start from the indicated line and work backwards towards the beginning of the file. Make sure that identifier names are spelled correctly and used consistently.

// ­­­­­­­­­­ DOCUMENT.CPP ­­­­­­­­­­
// The definition of class Document, for use in our word processor of PIP 9. 
// (With SYNTAX ERRORS)
#include <iostream.h> // for cin, cout
#include <fstream.h> // for ifstream, ofstream
#include "DOCUMENT.H" // for class Document
#include "OCSTRING.H" // for class OCString
#include "UTILITY.H" // for Swap

Document::Document()
// constructor creates a default document of 10 lines, and allocates storage for document
{
	maxLines=10;
	numberLines=1;
	text = new OCString[maxLines+1];
};

Document :: Document(int i)
// constructor sets maximum and current number of lines,
// and allocates storage for document
{
	maxLines = i;
	numberLines = 1;
	text = new OCString[maxLines+1];
};

Document :: ~Document()
// This destructor function for class Document de­allocates, first the OCStrings that 
// comprise the text of the document, and then the document's pointer to its text.
{
	delete [] text;
};

void Document :: InsertLine(int lineNum)
// This function inserts a new line of text at the position specified by parameter lineNum.
{
// Use class OCString and its overloaded I/O operators to read in a new line.
	OCString newLine;
	cout << "Type new line, followed by <Enter>:\n;
************unterminated string******************
	cin >> newLine;
************statement missing ;******************
	if(numberLines = maxLines) // if we have to expand text array
		Grow(); // do it
************possibly incorrect assignment******************
	if (lineNum < 1) // if line number is too small
		lineNum = 1; // assume we'll insert at start

	if (lineNum > numLines) // if line number is too big
		text[numberLines+1] = newLine; // insert at the end
***********undefined symbol numLines******************
	else 
	{ // normal insertion involves . . .
		ShiftLines(1,lineNum); // moving lines to make space, and
		text[lineNum] = newLine; // copying the line into our array
	}
	numberLines++; // in any case, we have one more line
}

void Document :: DeleteALine(int lineNum)
***********Document::DeleteALine is not a member of class Document******************
// This function removes a line of text from a document by shifting lines up.
{
	if((lineNum<1) || (lineNum > numberLines)) // if illegal line specified
		cout << "\n*** No such line.\n"; // don't assume anything!
	else
	{
		cout << "\nDeleting line " << lineNum << ". . .\n";
		ShiftLines(­1,lineNum); // shift lines in array up
		numberLines­­; // one fewer line now
	}
}

void Document :: ChangeLine(int lineNum)
// This function uses OCString assignment to replace an existing line with a new
// one provided by the user.
{
	if((lineNum<1) || (lineNum > numberLines)) // Can't replace a line
		cout << "\n*** No such line.\n"; // that doesn't exist
	else
	{
		cout << "Current line " << lineNum << ":" ; // Show the old line
		cout << '\n\t' << text[lineNum] << '\n';
		cout << "Enter new line: \n\t";
		cin >> text[lineNum]; // Read the new one
	}
}

void Document :: Display(int start, int finish)
// Display a range of lines from the document on the screen. The hardest part is making
// sure the line numbers make sense.
{
	if (start > finish)
		Swap (start, finish);
	if (start < 1)
		start = 1;
	if (( finish > numberLines) || (finish < 1))
		finish = numberLines;

// use loop and OCString's overloaded insertion operator
// to display each line of text in the range
	for (int lineNum=start; lineNum<=finish; lineNum++)
	{
		cout << "\n" << lineNum << ":\t";
		cout << text[lineNum];
	};
	cout << "\n\n";
}

void Document : Save()
***********declaration syntax error******************
// This function writes a document to a disk file.
// The user provides a file name, and the function
// opens that file and connects it to this program.
{
	char fileName[30];
	cout << "\n\nSave document in what file? Type full file name";
	cout << "\nfollowed by <Enter>: ";
	cin >> fileName;

	ofstream f(fileName);

// Now send the document one line at a time to the open file
// Notice that we start with line #1, to be consistent with the user's counting
	cout << "\nWriting file: " << fileName << '\n';
	for(int thisLine=1; thisLine <= numberLines; thisLine++)
		f << text[thisLine] << '\n'; 

// Close the file for safety's sake
	f.close();
}

void Document :: Retrieve()
// This function reads a document from a disk file. The user provides a file name, and the 
// function opens that file and connects it to this program.
{
	char fileName[30];
	cout << "\n\nOpen which document? Type full file name";
	cout << "\nfollowed by <Enter>: ";
	cin >> fileName;
	ifstream f(fileName);

// Since a file to be input must exist before reading it, we check to make sure it // does, and re­prompt if we can't open the file name specified
	while (!f)
	{
		cout << "\nSpecified file cannot be opened. Try again...";
		cout << "\n\nOpen which document? Type full file name";
		cout << "\nfollowed by <Enter>: ";
		cin >> fileName;
		ifstream f(fileName);
// Read the file in, one OCString at a time, starting with line #1 of the document 
// (leaving text[0] uninitialized ­­ that's OK because we don't access it!)
		cout << "\nReading file: " << fileName << '\n';
		numberLines = 1;
		while(f >> text[numberLines])
		{
			if (numberLines == maxLines)
				Grow();
			numberLines++;
		}
// the last read failed, so decrement the number of lines
		numberLines­­;
		f.close();
}

void Document :: ShiftLines(int direction, int start)
// This function moves lines of text one position up or down in our document, depending 
// upon the value of parameter direction (+1 means move lines down, making a space; ­1
// means move lines up, deleting a line). Parameter start indicates the line number
// from which the moving is to begin.
{
	int i;
	if (direction > 0)
// We want to make space, so copy lines "down" starting from the last line
		for(i = numberLines + 1; i >= start + 1; i­­)
			text[i]=text[i­1];
	else if (direction < 0)
// We want to remove a line, so move lines "up" starting from the specified line
		for(i = start; i <= numberLines ­ 1; i++)
			text[i]=text[i+1];
}

void Document :: Grow()
// Doubles the size of the dynamic array that holds the document's text. Called whenever // we run out of space when trying to insert a new line.
{
	int newSize = 2 * maxLines; // double the size of current text
	OCString* newText = new OCString[newSize+1]; 
// create a new array of that size
	for (int i = 1; i <= maxLines; i++) // copy all lines into new array
		newText[i] = text[i];
	delete[] text;
	text = newText; // make our document point to new text
	maxLines = newSize; // set new maximum size
}
************compound statement missing }******************

9.5 Linking Programs

Linking - integrating program libraries into a cohesive running program

1) forget to include a library file (identifiers not declared)

2) misspelled include file name

3) must have corresponding .CPP file for each .H file (identifiers undefined)

4) multiple declarations of include files (use #ifndef)

5) incorrect access control (functions that should be public are declared private)

RULES FOR AVOIDING LINKER ERRORS

1. Comment all include directives to indicate why it is being included.

2. Check file names in include directives to make sure that they match intended files.

3. Use #ifndef for all programmer-defined header files.

4. Define inline functions, global constants, and machine-dependent code in header files.

5. Comment all class declarations to describe access control.

9.6 RUNNING PROGRAMS

Run-time errors = fatal errors & logic errors

Use built-in debugger

// ­­­­­­­­­­ PIP9.CPP ­­­­­­­­­­
// A program (and a support function) that combines our User, OCString, and Document 
// classes to make a simple, line­oriented word processor.

#include <iostream.h> // for cin, cout
#include <ctype.h> // for toupper
#include "DOCUMENT.H" // for class Document (includes class OCString)
#include "USER2.H" // for class User (revised for this program)

void ProcessDocumentMenu(char c, int p1, int p2, Document d)
// This function serves the purpose of associating commands and parameters received
// from our menu with operations performed on Document objects.
{ // Choose an operation to perform
	switch(c) // based on the value of char c. . .
	{ case 'I': 	d.InsertLine(p1); // insert a line a position p1
			break;
	case 'C': 	d.ChangeLine(p1); // change the line at position p1
			break;
	case 'D': 	d.DeleteLine(p1); // delete the line at position p1
			break;
	case 'P': 	d.Display(p1, p2); // display the range of lines p1­p2
			break; 
	case 'Q': 	return; // simply return to quit
	};
}

void main()
{
	Document d; // creates a new, empty document
	char ans;

// Either leave the document blank, or read one in from disk
	cout << "\n\nType N to start a new document, or O to open an existing";
	cout << "\ndocument. Then hit <Enter>: ";
	cin >> ans;
	ans = toupper(ans);
	if (ans == 'O')
		d.Retrieve();

// Create our menu, and the necessary variables to read commands
	User u;
	char command;
	int param1, param2;

// The menu loop continues to get and process commands until
// a quit command is entered
	do
	{ 	u.ShowMenu();
		u.GetMenu(command, param1, param2);
		ProcessDocumentMenu(command, param1, param2, d);
	} while(command != 'Q');

// Give the user a chance to save the current document before terminating
	cout << "\n\nType S to save current document, or X to exit without";
	cout << "\nsaving. Then hit <Enter>: ";
	cin >> ans;
	ans = toupper(ans);
	if (ans == 'S')
		d.Save();
}

GENERAL DEBUGGING RULES

1. Become familiar and comfortable with the debugging tools.

2. When analyzing runtime errors, "play computer".

3. Start at point of error or incorrect behaviour and trace backwards.

4. In tracing backwards, examine every function and data item. Pay careful attention to values of array subscripts, formal and actual parameters and pointers.

5. Be systematic and thorough.

6. Avoid "fix and pray" approach to debugging.

Exercises

pp. 328-9 #1, #2, #5