Another way to write a class constructor

Frankly, I don't see that this has much advantage, but it's mentioned in the AP curriculum. Suppose I have the following constructors defined:


class Date

{

public:

	Date();

	Date(int m, int d, int y);

...

};

They can be implemented as:


Date::Date() : month(1), day(1), year(1940)

{

	// Empty body

}



Date::Date(int m, int d, int y) : month(m), day(d), year(y)

{

	// Empty body

}

This is called an initializer list.


Overloading operators: class definition

You can define arithmetic and comparison operators, like =, +, <, to work on class instances. The class definition and overloaded operator prototypes might look like:


class Fraction

{

public:

	Fraction();

	Fraction(int n, int d);

	int getNum();

	int getDenom();

	void setNum(int n);

	void setDenom(int d);



private:

	int num, denom;



};



bool operator ==(const Fraction& f1, const Fraction& f2);

bool operator <( const Fraction& f1, const Fraction& f2);



Fraction operator +(const Fraction& f1, const Fraction& f2);

Fraction operator *(const Fraction& f1, const Fraction& f2);

Fraction operator -(const Fraction& f);		// unary negation


Overloading operators: operator method definition

The definitions of the operators would then be written like those below. Note that these are simplified forms of the functions you'd really write for comparing/calculating with fractions.


bool operator ==(const Fraction& f1, const Fraction& f2)

{

	return (f1.getNum() == f2.getNum() && f1.getDenom() == f2.getDenom());

}



Fraction operator *( const Fraction& f1, const Fraction& f2)

{

	Fraction answer;



	answer.setNum( f1.getNum() * f2.getNum() );

	answer.setDenom( f1.getDenom() * f2.getDenom() );

	return answer;

}



Fraction operator -(const Fraction& f)

{

	Fraction answer;



	answer.setNum( -f.getNum() );

	answer.setDenom( f.getDenom() );

	return answer;

}


Overloaded operators with different types of arguments

Suppose you wanted to be able to add an integer to a fraction, or vice versa. You might include these prototypes and definitions:


Fraction operator +(const Fraction& f, int i);

Fraction operator +(int i, const Fraction& f);



Fraction operator +(const Fraction& f, int i)

{

	Fraction answer;



	answer.setNum( f.getNum() + i * f.getDenom() );

	answer.setDenom( f.getDenom() );

	return answer;

}



Fraction operator +(int i, const Fraction& f)

{

	// exactly the same body

}

So the following will work:


Fraction f(4, 7), g;

g = 2 + f;

g = f + 3;


Operator overloading rules

You can overload = (assignment), but not using this method. Usually the behavior of = is what you'd want anyway.


Lab:

First, rewrite your class constructors to use initializer lists.

Now, add the overloaded operators == and < to your Time class. What other operators might it be useful to design? How would you define addition or subtraction of two Time objects? What if you wanted to add an integral number of seconds to a Time object? How would the class prototype for that be expressed?


Overloading output operators

You can overload the input (>>) and output (<<) operators, but you must use a specific prototype. The prototype and definition for an output operator for fractions is below. The first parameter is an ostream object (cout is an ostream object), and the second is the fraction to be displayed. Notice that the ostream parameter is not a const. The << operator actually changes the private members of that object. Your overloaded output operator must return the value of that object.


ostream& operator <<(ostream& os, const Fraction& f);



ostream& operator <<(ostream& os, const Fraction& f)

{

	os << f.getNum();

	if (f.getDenom() != 1)

	os << "/" << f.getDenom();

	return os;

}

This allows you to write:


Fraction f(3, 5);



cout << f << endl;


Overloading input operators

The prototype and definition for an input operator for fractions is below. The first parameter this time is an istream object (cin is an istream object), and the second is the fraction object in which to store the input. Again, the istream parameter is not a const, and your overloaded output operator must return its value. The Fraction object isn't a const either, since the operator will change its value.


istream& operator >>(istream& is, Fraction& f);



istream& operator >>(istream& is, Fraction& f)

{

	int numerator, denominator;

	char symbol;

	is >> numerator >> symbol >> denominator;

	f.setNum(numerator);

	f.setDenom(denominator);

	return is;

}

This allows you to write:


Fraction f;



cin << f;


Lab:

Write and test input and output operators for your Time class. Use a format consistent with the representation you chose for times.


Array basics

Array declaration


element-type array-variable-name[size];

Examples:


int intArray[10];

char aLine[80];

double Averages[8];

bool Flags[4];

intArray would look something like this:

0123456789
          

Note that array indices start with 0, not 1!


Referring to array elements

Each space in the array can be used in exactly the same way as a single variable of the element type. To refer to a specific expression, we use a selection expression:


array-variable-name[index]

The index (subscript) may be an integer constant, but can be any expression which evaluates to an integer value.


ExamScores[0] = 45;

ExamScores[i] = 39;

x = ExamScores[4] * 2;

sum = ExamScores[k] + ExamScores[k + 1];

cout << "Score: " << ExamScores[3];



for (i = 0; i < 10; i++)

{

	ExamScores[i] = 0;

}


Program example: displaying above-average scores


#include "iostream.h"



const int Nstudents = 10;



int main()

{

	int i, ExamScores[Nstudents];

	double sum = 0.0, average;



	// Get input values

	cout << "Enter " << Nstudents << " scores:" << endl;

	for (i = 0; i < Nstudents; i++) {

		cin >> ExamScores[i];

		sum += ExamScores[i];

	}



	// Compute and display average

	average = sum / Nstudents;

	cout << "The average is " << average << endl;



	// Find and display numbers above the average

	for (i = 0; i < Nstudents; i++) {

		if (ExamScores[i] > average)

			cout << ExamScores[i] << " ";

	}

	cout << endl;



	return 0;

}


Out-of-range index errors

What do you think will happen when this code is executed?


int ExamScores[10];



for (i = 0; i <= 10; i++)

	cout << ExamScores[i] << endl;

C++ does not check to make sure your subscript values are within range. This is why we will be transitioning from using C/C++ style arrays to the AP template class apvector, which does provide subscript bounds checking.


Another example array program: probabilities of dice rolls


#include "iostream.h"

#include "iomanip.h"



int main()

{

	int prob[13];	// want to record counts for dice roll sums from 2 to 12

	int die1, die2, sum, i, j;



	// Initialize array

	for (i = 2; i <= 12; i++)

		prob[i] = 0;



	// Count occurrence of sums for possible rolls

	for (die1 = 1; die1 <= 6; die1++)

		for (die2 = 1; die2 <= 6; die2++)

			prob[die1 + die2]++;



	// Print out results

	for (i = 2; i <= 12; i++)

		cout << "Probability of sum of " << i << ": " << prob[i] << " in 36" << endl;

	cout << endl;



	// Print out bar graph

	for (i = 2; i <= 12; i++) {

		cout << setw(2) << i << ": ";

		for (j = 1; j <= prob[i]; j++)

			cout << "*";

		cout << endl;

	}



	return 0;



}


Example program output


Probability of sum of 2: 1 in 36

Probability of sum of 3: 2 in 36

Probability of sum of 4: 3 in 36

Probability of sum of 5: 4 in 36

Probability of sum of 6: 5 in 36

Probability of sum of 7: 6 in 36

Probability of sum of 8: 5 in 36

Probability of sum of 9: 4 in 36

Probability of sum of 10: 3 in 36

Probability of sum of 11: 2 in 36

Probability of sum of 12: 1 in 36



 2: *

 3: **

 4: ***

 5: ****

 6: *****

 7: ******

 8: *****

 9: ****

10: ***

11: **

12: *


Lab:

Write a program that reads 10 numbers into an array, then displays the contents of the array in reverse order.


Passing arrays as parameters

Naturally, arrays should be passed to functions using call-by-reference rather than call-by-value, to avoid making a duplicate copy of a potentially large array. However, you don't use the & to indicate an array parameter. Here is an example of a function with an array parameter.


void PrintArray(int anArray[], int size)

{

	for (int i = 0; i < size; i++)

		cout << anArray[i] << endl;

}

Specifying the size of the array inside the brackets is optional, so you can pass arrays of any length to the function. Typically, the size, if needed (and it usually is), is passed as a separate parameter. You'd call the function as follows:


int intArray1[100], intArray2[50];



PrintArray(intArray1, 100);

PrintArray(intArray2, 50);


Multi-dimensional arrays

Two-dimensional arrays are pretty common. Higher dimensions are possible but not quite as common. A two-dimensional array is called a matrix. The rows and columns are indexed, and each element in the matrix can be specified by giving the row and column which intersect at that element. For instance, a tic-tac-toe board:

  0 1 2
0
 X 
   
  O
1
2

The declaration of the board would look like this:


char board[3][3];

Normally, you consider the first subscript to be the row, the second to be the column. The position containing the 'X' is referenced as:


	board[0][1]

The position containing the 'O' is referenced as:


	board[2][2]


Passing two-dimensional arrays as parameters

Very similar to passing one-dimensional arrays, except that you must specify the size of the second dimension of the array. So the following two function starts would be legal:


void DisplayBoard(char board[3][3]) ...

void DisplayBoard(char board[][3]) ...

But the following two would not:


void DisplayBoard(char board[3][]) ...

void DisplayBoard(char board[][]) ...

Here's the body of the DisplayBoard function:


void DisplayBoard(char board[3][3]) {

	int row, column;



	for (row = 0; row < 3; row++) {

		if (row != 0)

			cout << "---+---+---" << endl;

		for (column = 0; column < 3; column++) {

			if (column != 0)

				cout << "|";

			cout << " " << board[row][column] << " ";

		}

		cout << endl;

	}

}


Out-of-range errors for two-dimensional arrays

Again, no automatic checking that your subscript values are within bounds. You can end up changing values in memory that you didn't intend to, with potentially disastrous consequences! We will instead be using the apmatrix template class. More interesting programs to write once we cover apvector and apmatrix. Even more fun when we get to the apstring class!

Exam topics:

Types of questions will be similar to quizzes, but free response questions will be graded more like the AP exam (no 8 out of 10 gimmes).