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.
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
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; }
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;
You can overload = (assignment), but not using this method. Usually the behavior of = is what you'd want anyway.
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?
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;
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;
Write and test input and output operators for your Time class. Use a format consistent with the representation you chose for times.
elementtype arrayvariablename[size];
Examples:
int intArray[10]; char aLine[80]; double Averages[8]; bool Flags[4];
intArray would look something like this:
0  1  2  3  4  5  6  7  8  9 
Note that array indices start with 0, not 1!
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:
arrayvariablename[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; }
#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; }
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.
#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; }
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: *
Write a program that reads 10 numbers into an array, then displays the contents of the array in reverse order.
Naturally, arrays should be passed to functions using callbyreference rather than callbyvalue, 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);
Twodimensional arrays are pretty common. Higher dimensions are possible but not quite as common. A twodimensional 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 tictactoe board:
0  1  2  
0 


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]
Very similar to passing onedimensional 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; } }
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).