PART 3
PART 3
Basics
Demonstrate how to define a function
Function Syntax
You have seen and used built-in functions like the length function
(my_string.length()). This unit deals with user-defined functions.
Functions are composed of two parts, the header and the body.
.guides/img/FunctionFull
Function Header
.guides/img/FunctionHeader
The function header usually contains a few key labels:
* void - Determines whether there is a return value or not for the function.
If the function has no return value, use void. If the function returns an
integer, use int, etc.
* GreetTwice - This is an example of a function name. See above for naming
conventions.
* () - Parentheses are required for all functions. Any parameters that the
function takes in will go into the parentheses but they are optional.
Function Body
.guides/img/FunctionBody
The function body is the list of actions the function performs. All of the
code for the function body should be enclosed within curly braces {} and
indented as a best practice to show association. This convention is similar
to how conditionals and loops are written.
Function Calls
Calling a Function
Copy the entire code below into the text editor to your left. Then click the
TRY IT button to see what happens.
#include <iostream>
using namespace std;
void GreetTwice() {
cout << "Hello" << endl;
cout << "Hello" << endl;
}
#include <iostream>
using namespace std;
void GreetTwice() {
cout << "Hello" << endl;
cout << "Hello" << endl;
}
int main() {
void GreetTwice() {
cout << "Hello" << endl;
cout << "Hello" << endl;
}
int main() {
GreetTwice();
}
challenge
important
IMPORTANT
You may have noticed that adding return 0 to main() in the code above
doesn’t really cause any changes to the program. In older versions of
C++, the command return 0 was required because the main() function
has a return type of int. This means that the function expects some
integer value to be returned at the end of the function. return 0 is
another way for the program to denote that the function has
successfully completed.
Newer versions of C++ can run int main() without return 0, however,
it is still a best practice to include it. Additionally, you should not
change the return type of main() to anything other than int. This is a
standard in C++ and should remain so.
#include <iostream>
using namespace std;
void GreetTwice() {
cout << "Goodbye" << endl;
cout << "Hello" << endl;
cout << "Hello" << endl;
}
int main() {
GreetTwice();
GreetTwice();
return 0;
}
Like how a regular C++ program runs, the function is executed line by line
from top to bottom. Thus, the order of statements within the function will
determine what actions are performed first, second, etc.
#include <iostream>
using namespace std;
int main() {
GreetTwice();
GreetTwice();
return 0;
}
void GreetTwice() {
cout << "Goodbye" << endl;
cout << "Hello" << endl;
cout << "Hello" << endl;
}
You will encounter an error such as GreetTwice was not declared in this
scope. Like how function definitions are read line by line from top to
bottom, functions are also read the same way. When the program gets to
the first GreetTwice(); within main(), it doesn’t know what to do because
the function has not been declared yet. Therefore, whenever you call a
function, make sure that you have already declared and defined it. Think of
a function as a vocabulary word, you would not use a vocabulary word that
you do not know the definition of.
Documentation
/**
* This is an example of C++ documentation
*
* @author FirstName LastName
* @version 1.0
*/
#include <iostream>
using namespace std;
/**
* This function greets the user twice
*
* @param specify parameters if any
* @return specify return value if any
*/
void GreetTwice() {
cout << "Hello" << endl;
cout << "Hello" << endl;
}
int main() {
GreetTwice();
return 0;
}
The C++ code documentation does not affect the output of the code.
However, it provides more clarity to how the function is used. Generally
speaking, the documentation for authors and program version goes at the
start of the program while the documentation for parameters and return
value goes directly before the declared function. Note that documentation
for the main() function is not required.
Doxygen Tool
There is an online tool called Doxygen that can help generate C++ code
documentation. For more information, you may visit the Doxygen website
at this link: Doxygen. However, for the purposes of this module, we will
only focus on self-created documentation. In particular, we will be using
mostly @param for parameters and @return for return values to document
our functions.
Learning Objectives: Parameters
Demonstrate how to define a function with parameters
Parameters
If a function contains parameters within its definition, they are required to
be present when the function is called. In the example below, the function,
Add(), adds two integer parameters together. Parameters are the types or
values located in between the parentheses. Multiple parameters are
separated by commas.
.guides/img/Parameters1
Copy and paste the following function into the text editor to your left
between the lines //add function definitions below this line and //add
function definitions above this line. DO NOT modify main() yet, or the
code will not print correctly!
/**
* This function adds two integers together
*
* @param num1 The first integer
* @param num2 The second integer
*/
void Add(int num1, int num2) {
cout << num1 + num2 << endl;
}
challenge
important
IMPORTANT
The number of arguments within main() should match the number
of parameters specified in the function. If there are three
parameters, then there should be three arguments as well.
The argument type should also match the parameter type. If the
function requires three integers, then the arguments should also
consist of three integers. You cannot provide a string argument for
an integer parameter, etc.
Order of Parameters
.guides/img/Parameters2
Much like how C++ programs run code from left to right and then top to
bottom, parameters are also read the same way. Because of this, the order
of parameters is important. The first argument in the function call will be
matched with the first parameter in the function header, the second
argument from the function call will be the second parameter in the
function header, etc. Copy the entire code below into the text editor and
then click TRY IT. What do you predict the output will be?
#include <iostream>
using namespace std;
/**
* This function adds the first two integers together,
* then subtracts the third integer
*
* @param num1 The first integer
* @param num2 The second integer
* @param num3 The third integer
*/
void AddSub(int num1, int num2, int num3) {
cout << num1 + num2 - num3 << endl;
}
int main() {
AddSub(5, 10, 15);
return 0;
}
challenge
/**
* This function divides one integer by the other
*
* @param num1 The first integer
* @param num2 The second integer
*/
void Divide(int num1, int num2) {
cout << num1 / num2 << endl;
}
int main() {
Divide(5, 0);
return 0;
}
.guides/img/TryCatchException
/**
* This function divides one integer by the other
*
* @param num1 The first integer
* @param num2 The second integer
*/
void Divide(int num1, int num2) {
try {
if (num2 == 0) {
throw runtime_error("Cannot divide by zero.");
}
else {
cout << num1 / num2 << endl;
}
}
catch (runtime_error& e) {
cout << e.what() << endl;
}
}
int main() {
Divide(5, 0);
return 0;
}
challenge
IMPORTANT
It’s important to note that when arguments are passed as parameters,
C++ tries to implicitly cast the arguments as the specified parameter
type(s) first. In the example above, both 14.5 and 2 get cast as ints.
Thus, 14.5 loses its trailing decimal places and becomes 14. On the
other hand, the string "2" cannot be implicitly cast as an int causing
the system to fail to compile. Note that you can only catch C++
exceptions, not compilation errors.
/**
* This function adds two integers together
*
* @param num1 The first integer
* @param num2 The second integer
*/
void Add(int num1, int num2) {
cout << num1 + num2 << endl;
}
/**
* This function adds three integers together
*
* @param num1 The first integer
* @param num2 The second integer
* @param num3 The third integer
*/
void Add(int num1, int num2, int num3) {
cout << num1 + num2 + num3 << endl;
}
int main() {
Add(3, 14);
return 0;
}
challenge
The two Add() functions above differ in the number of parameters they
have. Here is an example of two functions with the same name but
different parameter types.
/**
* This function adds two integers together
*
* @param num1 The first integer
* @param num2 The second integer
*/
void Add(int num1, int num2) {
cout << num1 + num2 << endl;
}
/**
* This function prints an integer followed
* by a string
*
* @param num1 The integer
* @param num2 The string
*/
void Add(int num1, string num2) {
cout << num1 << num2 << endl;
}
int main() {
Add(3, 14);
return 0;
}
challenge
/**
* This function prints all values of an array
*
* @param array A string array
*/
void PrintArray(string array[], int size) {
for (int i = 0; i < size; i++) {
cout << array[i] << endl;
}
}
int main() {
string names[] = {"Alan", "Bob", "Carol"};
int len = sizeof(names) / sizeof(names[0]);
PrintArray(names, len);
return 0;
}
challenge
int main() {
string names[] = {"Alan", "Bob", "Carol"};
PrintArray(names);
return 0;
}
important
IMPORTANT
When an array is passed as a function argument in C++, the system
treats the array as a pointer that points to the first element within the
array. Thus, the parameter string array[] is the same as string*
array. Due to this, knowledge of the size of the array is lost. This is why
it is a good practice to include an integer parameter for functions
involving arrays so that the size can be calculated and stored before
those functions are called.
Learning Objectives: Variable
Scope
Differentiate between global and local scope
Local Scope
Take a look at the code below. The first function declares the variable
my_var and then prints it. The second function also prints my_var. What do
you think the output will be?
void MyFunction1() {
string my_var = "Hello";
cout << my_var << endl;
}
void MyFunction2() {
cout << my_var << endl;
}
int main() {
MyFunction1();
MyFunction2();
return 0;
}
C++ returns an error such as error: ‘my_var’ was not declared in this
scope at the line containing cout << my_var << endl; within the second
function. This happens because variables declared inside a function have
local scope. Variables with local scope can only be used within that
function. Outside of that function, those local variables cannot be accessed.
In the image below, the light blue box represents the scope of my_var. Since
MyFunction2 (denoted in a light red box) is outside the scope of my_var, an
error occurs.
.guides/img/LocalScope
challenge
void MyFunction2() {
string my_var2 = "Hello";
cout << my_var2 << endl;
}
void MyFunction1() {
string my_var = "Hello";
cout << my_var << endl;
}
void MyFunction2() {
string my_var = "Bonjour";
cout << my_var << endl;
}
int main() {
MyFunction1();
MyFunction2();
return 0;
}
challenge
void MyFunction3() {
string my_var = "Hola";
cout << my_var << endl;
}
.guides/img/GlobalScope
void SayHello() {
cout << greeting << endl; //can access global variable
greeting
}
int main() {
SayHello();
return 0;
}
void SayHello() {
greeting = "Bonjour";
cout << greeting << endl;
}
int main() {
SayHello();
return 0;
}
challenge
void SayHello1() {
greeting = "Bonjour";
cout << greeting << endl;
}
void SayHello2() {
cout << greeting << endl;
}
int main() {
SayHello1();
SayHello2();
return 0;
}
Notice how in the code above the functions SayHello1() and SayHello2()
end up printing the same output. The result of greeting within SayHello2()
is affected by the modification of greeting within SayHello1().
Global vs. Local Scope
void PrintScope() {
string my_var = "local scope";
cout << my_var << endl;
}
int main() {
PrintScope();
cout << my_var << endl;
}
void PrintScope() {
my_var = "local scope";
cout << my_var <<endl;
}
int main() {
PrintScope();
cout << my_var << endl;
}
challenge
int main() {
PrintScope(my_var);
cout << my_var << endl;
}
void PrintScope() {
kMyConstant = "I CAN'T CHANGE";
cout << kMyConstant << endl;
}
int main() {
PrintScope();
cout << kMyConstant << endl;
}
challenge
/**
* This function adds 5 to an integer
*
* @param num An integer
* @return The integer added to 5
*/
int AddFive(int num) {
return num + 5;
}
int main() {
AddFive(10);
return 0;
}
You’ll notice the program no longer prints anything to the screen, which is
the cause for the message, Command was successfully executed.. This
happens because the function only adds 5 to whatever parameter is passed
to the function and then returns it internally. To see the result, explicitly tell
C++ to print the return value of the function to the screen.
/**
* This function adds adds 5 to an integer
*
* @param num An integer
* @return The integer added to 5
*/
int AddFive(int num) {
return num + 5;
}
int main() {
int newNum = AddFive(10);
cout << newNum << endl;
return 0;
}
challenge
Returning Values
Functions can return any value in C++ — ints, doubles, strings, vectors, etc.
/**
* This function adds two integers together
*
* @param x The first integer
* @param y The second integer
* @return x added to y
*/
int ReturnInt(int x, int y) { //int function
return(x + y);
}
/**
* This function adds two doubles together
*
* @param x The first double
* @param y The second double
* @return x added to y
*/
double ReturnDouble(double x, double y) { //double function
return(x + y);
}
/**
* This function adds two strings together
*
* @param x The first string
* @param y The second string
* @return x added to y
*/
string ReturnString(string x, string y) { //string function
return(x + y);
}
challenge
int main() {
vector<int> numbers;
numbers.push_back(1);
numbers.push_back(2);
numbers.push_back(3);
numbers.push_back(4);
numbers.push_back(5);
return 0;
}
Alternatively, you can also type the function as void which results in
fewer lines of code.
void MultiplyFive(vector<int>& my_list) {
vector<int> new_list;
for (auto a : my_list) {
new_list.push_back(a * 5);
}
for (auto a : new_list) {
cout << a << endl;
}
}
int main() {
vector<int> numbers;
numbers.push_back(1);
numbers.push_back(2);
numbers.push_back(3);
numbers.push_back(4);
numbers.push_back(5);
MultiplyFive(numbers);
return 0;
}
Helper Functions
Helper Functions
Helper functions are functions that are called from within other functions.
Take, for example, the formula for calculating the area of a circle:
.guides/img/Radius
/**
* This function finds the radius of a circle given
* two coordinate points
*
* @param x1 A double of the first x-coordinate
* @param y1 A double of the first y-coordinate
* @param x2 A double of the second x-coordinate
* @param y2 A double of the second y-coordinate
* @return The radius of a circle in double
*/
double FindRadius(double x1, double y1, double x2, double y2) {
return(sqrt(pow(x2 - x1, 2) + pow(y2 - y1, 2)));
}
/**
* This function finds the area of a circle given
* two coordinate points
*
* @param x1 A double of the first x-coordinate
* @param y1 A double of the first y-coordinate
* @param x2 A double of the second x-coordinate
* @param y2 A double of the second y-coordinate
* @return The area of a circle in double
*/
double FindArea(double x1, double y1, double x2, double y2) {
return(M_PI * pow(FindRadius(x1, y1, x2, y2), 2));
}
int main() {
cout << FindArea(0.0, 0.0, 4.0, 4.0) << endl;
return 0;
}
info
Math Functions
Note that in order to perform certain functions such as finding a
square or an exponent, we need to include <math.h> in our header as
well as to define M_PI which represents pi. If you remove these from
the program header, the math functions associated with <math.h> will
no longer work. In essence, these functions also serve as helper
functions.
Lab: Parameters
Function Header
First, we need to set up the function header. As usual, we will start off with
our return type. Since the result is simply printing the slope, we will use
void as the return type. Additionally, we’ll name the function GetSlope().
void GetSlope() {
}
Parameters
The function should take 4 doubles as parameters named x1, y1, x2, and y2.
int main() {
GetSlope(3, 2, 5, 6);
return 0;
}
Code
/**
* This function prints the slope between two sets
* of coordinate points
*
* @param x1 A double of the first x-coordinate
* @param y1 A double of the first y-coordinate
* @param x2 A double of the second x-coordinate
* @param y2 A double of the second y-coordinate
* @return No return value
*/
void GetSlope(double x1, double y1, double x2, double y2) {
cout << (y2 - y1) / (x2 - x1) << endl;
}
int main() {
GetSlope(3, 2, 5, 6);
return 0;
}
Lab: Variable Scope
Global Variables
First, let’s add some global variables to our program.
double input1;
double input2;
double input3;
double input4;
int main() {
cout << "Enter first x coordinate: " << endl;
cin >> input1;
cout << "Enter first y coordinate: " << endl;
cin >> input2;
cout << "Enter second x coordinate: " << endl;
cin >> input3;
cout << "Enter second y coordinate: " << endl;
cin >> input4;
You’ll notice that you have access to the Terminal which you will use to
input any coordinate points you want. If you enter 3, 2, 5, and 6
respectively, you should get 2 since cout removes all trailing zeros. Click the
TRY IT button to compile and run the program.
Code
double input1; //global
double input2; //global
double input3; //global
double input4; //global
/**
* This function prints the slope between two sets
* of coordinate points by calculating their coordinate
* changes separately
*
* @param x1 A double of the first x-coordinate
* @param y1 A double of the first y-coordinate
* @param x2 A double of the second x-coordinate
* @param y2 A double of the second y-coordinate
* @return No return value
*/
void GetSlope(double x1, double y1, double x2, double y2) {
double y_change = y2 - y1;
double x_change = x2 - x1;
cout << y_change / x_change << endl;
}
int main() {
cout << "Enter first x coordinate: " << endl;
cin >> input1;
cout << "Enter first y coordinate: " << endl;
cin >> input2;
cout << "Enter second x coordinate: " << endl;
cin >> input3;
cout << "Enter second y coordinate: " << endl;
cin >> input4;
info
Program Flow
After the program is initiated, the global variables will be created first.
Next, main() will run. Although commonly written last, main() will
always be the first function to run by default in C++. The lines of code
within main() will be executed in the order of their appearance.
Lab: Return Values
Returning a Value
When the result of a function is simply a print statement, it is considered to
be a void function. void functions do not have a return type, meaning they
do not return data back to the user. To return data, use the keyword return
followed by the data. Note that functions with return must be declared
with same data type as the data that they return. For example, a function
that returns an double must be declared in the header as a double function.
Notice how we need to use to_string() to convert our doubles into strings.
Completing the Program
Now just copy over the rest of the program that we had previously written.
double input1;
double input2;
double input3;
double input4;
/**
* This function returns the slope between two sets
* of coordinate points by calculating their coordinate
* changes separately
*
* @param x1 A double of the first x-coordinate
* @param y1 A double of the first y-coordinate
* @param x2 A double of the second x-coordinate
* @param y2 A double of the second y-coordinate
* @return A string expression of the slope in rise / run format
*/
string GetSlope(double x1, double y1,
double x2, double y2) {
double y_change = y2 - y1;
double x_change = x2 - x1;
return to_string(y_change) + " / " + to_string(x_change);
}
int main() {
cout << "Enter first x coordinate: " << endl;
cin >> input1;
cout << "Enter first y coordinate: " << endl;
cin >> input2;
cout << "Enter second x coordinate: " << endl;
cin >> input3;
cout << "Enter second y coordinate: " << endl;
cin >> input4;
double input1;
double input2;
double input3;
double input4;
/**
* This function returns the slope between two sets
* of coordinate points by calculating their coordinate
* changes separately
*
* @param x1 A double of the first x-coordinate
* @param y1 A double of the first y-coordinate
* @param x2 A double of the second x-coordinate
* @param y2 A double of the second y-coordinate
* @return A string expression of the slope in rise / run format
*/
string GetSlope(double x1, double y1,
double x2, double y2) {
double y_change = y2 - y1;
double x_change = x2 - x1;
return to_string(y_change) + " / " + to_string(x_change);
}
int main() {
cout << "Enter first x coordinate: " << endl;
cin >> input1;
cout << "Enter first y coordinate: " << endl;
cin >> input2;
cout << "Enter second x coordinate: " << endl;
cin >> input3;
cout << "Enter second y coordinate: " << endl;
cin >> input4;
/**
* This function returns the difference in y values
*
* @param y1 A double of the first y-coordinate
* @param y2 A double of the second y-coordinate
* @return The difference of y1 and y2 as a double
*/
double GetRise(double y1, double y2) {
return y2 - y1;
}
/**
* This function returns the difference in x values
*
* @param x1 A double of the first x-coordinate
* @param x2 A double of the second x-coordinate
* @return The difference of x1 and x2 as a double
*/
double GetRun(double x1, double x2) {
return x2 - x1;
}
Above, we have two functions. One that calculates the rise of a slope and
another that calculates the run of a slope. These two helper functions will
come in handy in out later slope calculations.
/**
* This function returns the slop in fraction form
*
* @param x1 A double of the first x-coordinate
* @param y1 A double of the first y-coordinate
* @param x2 A double of the second x-coordinate
* @param y2 A double of the second y-coordinate
* @return A string expression of the slope in rise / run format
*/
string GetSlopeFraction(double x1, double y1,
double x2, double y2) {
return to_string(GetRise(y1, y2)) + " / " +
to_string(GetRun(x1, x2));
//need to convert doubles to strings!
}
double input1;
double input2;
double input3;
double input4;
/**
* This function returns the difference in y values
*
* @param y1 A double of the first y-coordinate
* @param y2 A double of the second y-coordinate
* @return The difference of y1 and y2 as a double
*/
double GetRise(double y1, double y2) {
return y2 - y1;
}
/**
* This function returns the difference in x values
*
* @param x1 A double of the first x-coordinate
* @param x2 A double of the second x-coordinate
* @return The difference of x1 and x2 as a double
*/
double GetRun(double x1, double x2) {
return x2 - x1;
}
/**
* This function returns the slope in decimal form
*
* @param x1 A double of the first x-coordinate
* @param y1 A double of the first y-coordinate
* @param x2 A double of the second x-coordinate
* @param y2 A double of the second y-coordinate
* @return A double expression of the slope
*/
double GetSlopeDecimal(double x1, double y1,
double x2, double y2) {
return GetRise(y1, y2) / GetRun(x1, x2);
}
/**
* This function returns the slop in fraction form
*
* @param x1 A double of the first x-coordinate
* @param y1 A double of the first y-coordinate
* @param x2 A double of the second x-coordinate
* @param y2 A double of the second y-coordinate
* @return A string expression of the slope in rise / run format
*/
string GetSlopeFraction(double x1, double y1,
double x2, double y2) {
return to_string(GetRise(y1, y2)) + " / " +
return to_string(GetRise(y1, y2)) + " / " +
to_string(GetRun(x1, x2));
}
int main() {
cout << "Enter first x coordinate: " << endl;
cin >> input1;
cout << "Enter first y coordinate: " << endl;
cin >> input2;
cout << "Enter second x coordinate: " << endl;
cin >> input3;
cout << "Enter second y coordinate: " << endl;
cin >> input4;
Existing Code:
#include <iostream>
#include <vector>
using namespace std;
Requirements
You should not make any changes to the code that already exists. If you
accidentally delete any existing code, you can copy and paste the entire
program from above.
You can use any number of additional functions you’d like but you
must include at least the function called SayHello() in your code.
create
create
What is Recursion?
Solving a coding problem with functions involves breaking down the
problem into smaller problems. When these smaller problems are
variations of the larger problem (also know as self-similar), then recursion
can be used. For example, the mathematical function factorial is self-
similar. Five factorial (5!) is calculated as 5 * 4 * 3 * 2 * 1. Mouse over
the image below to see that 5! is really just 5 * 4!, and 4! is really just 4 *
3! and so on.
/**
* Calculates factorial using recursion
*
* @param n, integer
* @return factorial of n, integer
*/
int Factorial(int n) {
if (n == 1) {
return 1;
}
else {
return n * Factorial(n - 1);
}
}
int main() {
Optional: Click the Code Visualizer link below to see how C++ handles this
recursive function behind-the-scenes.
Code Visualizer
.guides/img/CppRecursion
The base case is the most important part of a recursive function. Without it,
the function will never stop calling itself. Like an infinite loop, C++ will stop
the program with an error. Replace the function in your code with the one
below and see what happens.
/**
* This recursive function returns an error
*
* @param n, integer
* @return factorial of n, integer
*/
int Factorial(int n) {
return n * Factorial(n - 1);
}
Code Visualizer
Always start with the base case when creating a recursive function. Each
time the function is called recursively, the program should get one step
closer to the base case.
challenge
int Factorial(int n) {
if (n == 1) { //base case
return 1;
}
else { //recursive case
return n * Factorial(n - 1);
}
}
Try to modify the base case so that Factorial(0) does not result in
an error.
Solution
int Factorial(int n) {
if (n <= 0) {
return 1;
}
else {
return n * Factorial(n - 1);
}
}
Code Visualizer
Fibonacci Sequence
Fibonacci Number
A Fibonacci number is a number in which the current number is the sum
of the previous two Fibonacci numbers.
Fibonacci Sequence
/**
* @param n, integer
* @return Fibonacci number of n, integer
*/
int Fibonacci(int n) {
if (n <= 1) {
return n;
}
else {
return(Fibonacci(n-1) + Fibonacci(n-2));
}
}
int main() {
cout << Fibonacci(3) << endl;
return 0;
}
Code Visualizer
challenge
Fibonacci Sequence
Fibonacci numbers are most often talked about as a sequence. The main()
function below adds the functionality of printing a Fibonacci sequence of a
predetermined length. Replace your current main() function with the one
below and TRY IT.
int main() {
int fibonacci_length = 4;
for (int i = 0; i < fibonacci_length; i++) {
cout << Fibonacci(i) << endl;
}
return 0;
}
Code Visualizer
challenge
NOTE: long is a data type that can hold much larger values than int can.
Thus, for larger numbers, long is necessary.
long Fibonacci(long n) {
static vector<long> v = {0, 1};
if (n < v.size())
return v.at(n);
else {
v.push_back(Fibonacci(n - 1) + Fibonacci(n - 2));
return v.at(n);
}
}
int main() {
int fib_length = 50;
for (int i = 0; i < fib_length; i++) {
cout << Fibonacci(i) << endl;
}
return 0;
}
Lab 1
Recursive Tree
Trees can be drawn recursively. Draw a branch. At the end of the branch,
draw two smaller branches with one to the left and the other to the right.
Repeat until a certain condition is true. This program will walk you
through drawing a tree in this way.
.guides/img/TurtleGraphicsFlow
Program Instructions
Let’s start by creating a canvas screen and a Turtle object tina in main() to
allow the Turtle object to move around on. Feel free to edit the screen size
so that it fits comfortably on your monitor. You’ll also notice that there is
function called RecursiveTree(). This function takes three parameters,
branch_length, angle, and t.
Note that when passing an object (like a Turtle), you should should pass it
as a reference using the & symbol (i.e. Turtle& t).
/**
* @param branch_length An integer
* @param angle The angle of degree
* @param t A Turtle object
* @return A drawing symbolizing a tree branch
*/
void RecursiveTree(int branch_length, int angle, Turtle& t) {
screen.exitonclick();
return 0;
}
The base case for this function is a bit different. In previous examples, if
the base case is true a value was returned. The function RecursiveTree()
does not return a value, it draws on the screen instead. So the base case
will be to keep recursing as long as branch_length is greater than some
value. Define the base case as branch_length as being greater than 5.
if (branch_length > 5) {
Start drawing the tree by going forward and turning right. Then call
RecursiveTree() again, but reduce branch_length by 15. The code should
run, but the tree will not look like a tree. It looks more like a curve made of
a series of line segments decreasing in size.
if (branch_length > 5) {
t.forward(branch_length);
t.right(angle);
RecursiveTree(branch_length - 15, angle, t);
}
In main(), let’s call the RecursiveTree() function and pass in some initial
values.
int main(int argc, char** argv) {
screen.exitonclick();
return 0;
The next step is to draw the branch that goes off to the left. Since the turtle
turned to the right the number of degrees that the parameter angle
represents, the turtle needs to turn to the left twice the degrees of angle.
Turning to the left angle will put the turtle back at its original heading. The
turtle needs to go further to the left. Then draw another branch whose
length is reduced by 15.
if (branch_length > 5) {
t.forward(branch_length);
t.right(angle);
RecursiveTree(branch_length - 15, angle, t);
t.left(angle * 2);
RecursiveTree(branch_length - 15, angle, t);
}
The tree is looking better, but there are two more things that need to be
done. First, put the turtle back to its original heading by turning right angle
degrees. Then go backwards the length of the branch. If you tweak some of
the arguments when calling the RecursiveTree() function, you might notice
the tree changing.
void RecursiveTree(int branch_length, int angle, Turtle& t) {
if (branch_length > 5) {
t.forward(branch_length);
t.right(angle);
RecursiveTree(branch_length - 15, angle, t);
t.left(angle * 2);
RecursiveTree(branch_length - 15, angle, t);
t.right(angle);
t.backward(branch_length);
}
challenge
Code Sample
/**
* @param branch_length An integer
* @param angle The angle of degree
* @param t A Turtle object
* @return A drawing symbolizing a tree branch
*/
void RecursiveTree(int branch_length, int angle, Turtle& t) {
if (branch_length > 5) {
t.forward(branch_length);
t.right(angle);
RecursiveTree(branch_length - 5, angle, t);
t.left(angle * 2);
RecursiveTree(branch_length - 5, angle, t);
t.right(angle);
t.backward(branch_length);
}
screen.exitonclick();
return 0;
}
Lab 2
Hilbert Curve
/**
* @param dist, integer
* @param rule, integer
* @param angle, integer
* @param depth, integer
* @param t, Turtle
* @return A drawing of the Hilbert Curve
*/
void Hilbert(int dist, int rule, int angle, int depth, Turtle&
t) {
screen.exitonclick();
return 0;
The base case for the function is when depth is 0. Another way to think
about the base case is that if depth is greater than 0, keep drawing the
fractal. Use if (depth > 0) as the base case. Also, there are two rules for
the turtle. Ask if rule is equal to 1 or if it is equal to 2.
void Hilbert(int dist, int rule, int angle, int depth, Turtle&
t) {
if (depth > 0) {
if (rule == 1) {
//rule1 code
}
if (rule == 2) {
//rule2 code
}
}
The code continues with if rule is equal to 1, then the turtle is going to turn
left, recursively call the Hilbert() function with rule set to 2, go forward,
turn right, recursively call the Hilbert() function with rule set to 1, go
forward, recursively call the Hilbert() function with rule set to 1, turn
right, and finally move forward. Because the base case is based on depth, it
must be reduced by 1 each time the Hilbert() function is called
recursively.
if (rule == 1) {
//rule1 code
t.left(angle);
Hilbert(dist, 2, angle, depth - 1, t);
t.forward(dist);
t.right(angle);
Hilbert(dist, 1, angle, depth - 1, t);
t.forward(dist);
Hilbert(dist, 1, angle, depth - 1, t);
t.right(angle);
t.forward(dist);
Hilbert(dist, 2, angle, depth - 1, t);
t.left(angle);
}
If rule is equal to 2, then the code is almost the inverse of when rule is
equal to 1. The turtle will still go forward, but left turns become right turns,
right turns become left turns, and recursive calls to Hilbert() will use 2
instead of 1 for the rule parameter (and vice versa).
if (rule == 2) {
//rule2 code
t.right(angle);
Hilbert(dist, 1, angle, depth - 1, t);
t.forward(dist);
t.left(angle);
Hilbert(dist, 2, angle, depth - 1, t);
t.forward(dist);
Hilbert(dist, 2, angle, depth - 1, t);
t.left(angle);
t.forward(dist);
Hilbert(dist, 1, angle, depth - 1, t);
t.right(angle);
}
Finally, call the Hilbert() function in main() and run the program to see
the fractal.
screen.exitonclick();
return 0;
Note: You might need to adjust your TurtleScreen’s size to capture all
of the output.
Sample Code
/**
* @param dist, integer
* @param rule, integer
* @param depth, integer
* @param t, Turtle
* @return A drawing of the Hilbert Curve
*/
void Hilbert(int dist, int rule, int angle, int depth, Turtle&
t) {
if (depth > 0) {
if (rule == 1) {
//rule1 code
t.left(angle);
Hilbert(dist, 2, angle, depth - 1, t);
t.forward(dist);
t.right(angle);
Hilbert(dist, 1, angle, depth - 1, t);
t.forward(dist);
Hilbert(dist, 1, angle, depth - 1, t);
t.right(angle);
t.forward(dist);
Hilbert(dist, 2, angle, depth - 1, t);
t.left(angle);
}
if (rule == 2) {
//rule2 code
t.right(angle);
Hilbert(dist, 1, angle, depth - 1, t);
t.forward(dist);
t.left(angle);
Hilbert(dist, 2, angle, depth - 1, t);
t.forward(dist);
Hilbert(dist, 2, angle, depth - 1, t);
t.left(angle);
t.forward(dist);
Hilbert(dist, 1, angle, depth - 1, t);
t.right(angle);
}
}
screen.exitonclick();
return 0;
}
Lab Challenge
Lab Challenge
Problem
Write a recursive function called RecursivePower() that takes two integers
as parameters. The first parameter is the base number and the second
parameter is the exponent. Return the base number parameter to the
power of the exponent.
DO NOT edit any existing code or you will not receive credit for your work!
#include <iostream>
using namespace std;
Expected Output
* If the function call is RecursivePower(5, 3), then the function will return
125.
* If the function call is RecursivePower(4, 5), then the function will return
1024.
Expected Output
216
Expected Output
1
Learning Objectives: Classes and
Objects
Define a constructor
challenge
int arr[1];
cout << "arr is a: " << typeid(arr).name() << endl;
The resulting output says arr is a: A1_i. A1 stands for one dimensional
array and i stands for integer.
Vocabulary
In the text above, the words “class” and “object” are used in an almost
interchangeable manner. There are many similarities between classes and
objects, but there is also an important difference. Working with objects has
a lot of specialized vocabulary.
Classes - Classes are a collection of data and the actions that can modify the
data. Programming is a very abstract task. Classes were created to give
users a mental model of how to think about data in a more concrete way.
Classes act as the blueprint. They tell C++ what data is collected and how it
can be modified.
Instance - Another way that programmers talk about objects is to say that
an object is an instance of a particular class. For example, s is an instance
of the string class.
.guides/img/objects/cpp_class_v_object.png
User-Defined Objects
Defining an Object
Assume you want to collect information about actors. Creating a class is a
good way to keep this data organized. The class keyword is used to define
a class. For now, do not add anything as the body of the class.
class Actor {
};
Naming classes
Classes are just the blueprint. To you use a class, you need to instantiate an
object. Here is an object to represent Helen Mirren. Be sure to put this code
in the main function.
Actor helen;
So you now have helen, which is now an instance of the Actor class.
Adding Attributes
The point of having a class is to collect information and define actions that
can modify the data. The Actor class should contain things like the name of
the actor, notable films, awards they have won, etc. These pieces of
information related to a class are called attributes. Attributes are declared
in the class itself. The example below adds the first_name and last_name
attributes which are both strings. Notice that a keyword is also required
public. public is considered to be an access specifier which determines
how accessible the attributes are from outside the class. Adding this
keyword just means that the attributes first_name and last_name are
readily accessible. For now, we will be using public as the access specifier
for our classes.
class Actor {
public:
string first_name;
string last_name;
};
You can change the value of an attribute with the assignment operator,
object_name.attribute = attribute_value. Notice that you always use
object_name.attribute to reference an attribute. This is called dot notation.
Once an attribute has a value, you can treat it like any other variable. Add
the following code to the main function. You are assigning values to the
attributes fist_name and last_name, and then printing these values.
Actor helen;
helen.first_name = "Helen";
helen.last_name = "Mirren";
cout << helen.first_name + ' ' + helen.last_name << endl;
Note that many objects in C++ cannot be printed directly, thus cout <<
helen << endl; resulted in an error. When printing objects, be sure to
reference their attributes.
The Constructor
class Actor {
public:
string first_name;
string last_name;
string birthday;
int total_films;
int oscar_nominations;
int oscar_wins;
};
Now create an object for Helen Mirren with values for each attribute.
Adding each attribute individually requires several lines of code. This is
especially true if you have more than one instance of the Actor class.
Actor helen;
helen.first_name = "Helen";
helen.last_name = "Mirren";
helen.birthday = "July 26";
helen.total_films = 80;
helen.oscar_nominations = 4;
helen.oscar_wins = 1;
cout << helen.first_name + ' ' + helen.last_name << endl;
The class Actor creates a class and its attributes. It does not assign value to
any attributes; the user has to do this. A class is supposed to be a blueprint.
It should lay out all of the attributes and their values for the user. Classes
can do this when you use the constructor.
The Constructor
The constructor is a special function for a class. Its job is to assign value for
attributes associated with the object. These attributes can also be called
instance variables. In C++, the constructor is the class name, parentheses,
and curly brackets. Inside of the constructor, give attributes their values.
class Actor {
public:
string first_name;
string last_name;
string birthday;
int total_films;
int oscar_nominations;
int oscar_wins;
Actor() {
first_name = "Helen";
last_name = "Mirren";
birthday = "July 26";
total_films = 80;
oscar_nominations = 4;
oscar_wins = 1;
}
};
Actor helen;
cout << helen.first_name + ' ' + helen.last_name << endl;
class Actor {
public:
string first_name;
string last_name;
string birthday;
int total_films;
int oscar_nominations;
int oscar_wins;
Actor() {
first_name = "Helen";
last_name = "Mirren";
birthday = "July 26";
total_films = 80;
oscar_nominations = 4;
oscar_wins = 1;
}
};
Now instantiate two Actor objects, one for Helen Mirren and the other for
Tom Hanks. Print the fist_name and last_name attributes for each object.
Actor helen;
Actor tom;
cout << helen.first_name << ' ' << helen.last_name << endl;
cout << tom.first_name << ' ' << tom.last_name << endl;
class Actor {
public:
string first_name;
string last_name;
string birthday;
int total_films;
int oscar_nominations;
int oscar_wins;
Actor(string fn, string ln, string bd, int tf, int on, int ow)
{
first_name = fn;
last_name = ln;
birthday = bd;
total_films = tf;
oscar_nominations = on;
oscar_wins = ow;
}
};
When you instantiate the two Actor objects, you can pass the constructor
the relevant information for both Helen Mirren and Tom Hanks. The code
should now print the correct first and last names.
Code
Your code for the object representing Denzel Washington should look
something like this:
Default Values
We can assume that the average actor probably has not been nominated or
won an Oscar. So instead of making these attributes parameters for the
constructor, we can give them the default value of 0. These attributes can
always be updated later on.
//add class definitions below this line
class Actor {
public:
string first_name;
string last_name;
string birthday;
int total_films;
int oscar_nominations;
int oscar_wins;
You can update the attributes once the object has been instantiated if need
be.
helen.oscar_nominations = 4;
helen.oscar_wins = 1;
Copying an Object
In C++, you can initialize a new object to an existing one to create a clone.
What do you thin the code below does?
class ComicBookCharacter {
public:
string name;
int age;
string type;
};
int main() {
ComicBookCharacter a;
a.name = "Calvin";
a.age = 6;
a.type = "human";
ComicBookCharacter b = a;
a.name = "Hobbes";
You’ll notice that the initializing one object to another created an exact
copy of the original object. Each object is still treated as separate objects
though so you can still freely change the attribute of one object without
affecting another.
challenge
class ClassName {
public:
type attribute1;
type attribute2;
};
1. All objects are created inside classes. The first step is use the keyword
class.
2. Give the class a name which is used to construct an object.
3. The keyword public: is needed in order to provide access to object
attributes that you define later on.
4. Give your object as many attributes as needed in order to make full use
of your object.
5. All attributes go within curly braces {} after the class name declaration.
Make sure to end the closing curly brace with a semi-colon ;.
Student Object
Let’s define an object called Student. When we think of a student, some
information or attributes that we might associate with students are:
* Name
* Student ID number
* Major
* GPA
* Age
* Year of graduation
Once we’ve narrowed down what attributes we want associated with our
object, we can start defining them.
class Student {
public:
string name;
int ID;
string major;
double GPA;
int age;
int YOG;
};
Student andy;
andy.name = "Andy";
andy.ID = 123456;
andy.major = "Computer Science";
andy.GPA = 3.45;
andy.age = 22;
andy.YOG = 2021;
A Default Constructor
A constructor works similarly to a function in that you can define
parameters within the constructor. Then when it’s time to call the
constructor, you can just simply give it the appropriate arguments and the
object can be made. Let’s return to our Student object.
//add class definitions below this line
class Student {
public:
string name;
int ID;
string major;
double GPA;
int age;
int YOG;
Student() {
name = "Andy";
ID = 123456;
major = "Computer Science";
GPA = 3.45;
age = 22;
YOG = 2021;
}
};
int main() {
Student mary;
cout << mary.name << "'s student ID is " << mary.ID << "." <<
endl;
mary.name = "Mary";
mary.GPA = 3.78;
cout << mary.name << " has a current GPA of " << mary.GPA <<
"." << endl;
return 0;
Before, we had to use dot notation to assign values to our object. But having
the constructor, we can build it in a way that it will have default values
when the object is created. However, notice how the object mary has all of
the attributes of andy.
Constructors with Parameters
The default constructor makes all objects an andy object when they are
built. To change the attributes of the object, we can still use dot notation
(i.e. mary.name = "Mary";). However, most Students are unique and to have
to reassign value every time a default constructor is used can still be a
small challenge.
class Student {
public:
string name;
int ID;
string major;
double GPA;
int age;
int YOG;
int main() {
cout << mary.name << " is a student in the " << mary.major <<
" department." << endl;
cout << mary.name << " is a junior while " << andy.name << "
is a senior." << endl;
return 0;
Lab Challenge
Create the variable dog1, and instantiate it as an object of the Dog class. This
dog’s name is Marceline and she is a German Shepherd. Create the variable
dog2 and make it a copy of dog1. dog2 should be named Cajun and have the
breed Belgian Malinois.
Your goal for this assignment is to design the class Dog so that the above can
be implemented successfully.
Expected Output
Test your code by printing the name and breed of each dog to make sure they
fulfill the requirements above. Most importantly, the third print statement
will print false.
cout << dog1.name << " " << dog1.breed << endl;
cout << dog2.name << " " << dog2.breed << endl;
if (dog1.name == dog2.name && dog1.breed == dog2.breed) {
cout << boolalpha << true;
}
else {
cout << boolalpha << false;
}
Learning Objectives: Mutability
Mutability
Objects are mutable, which means that objects (specifically their attributes)
can change value. Think of a video game; the main character in the game is
constantly changing. It could be their position on the screen, the score,
their health, the items in their inventory, etc. Imagine a simple class called
Player. A newly instantiated Player object has a health of 100, a score of 0,
and starts on level 1. This object can lose health, increase their score, and
advance levels.
class Player {
public:
int health;
int score;
int level;
Player() {
health = 100;
score = 0;
level = 1;
}
};
Print out the attributes of player1. Then change each attribute and print
out the attributes again.
//add code below this line
Player player1;
cout << "This player has " << player1.health << " health, a
score of " << player1.score;
cout << ", and is on level " << player1.level << "." << endl;
player1.health -= 10;
player1.score += 25;
player1.level += 1;
cout << "This player has " << player1.health << " health, a
score of " << player1.score;
cout << ", and is on level " << player1.level << "." << endl;
challenge
player1.health = 0;
cout << "This player has " << player1.health << " health.
Game over." << endl;
void PrintPlayer(Player p) {
cout << "This player has " << p.health << " health, a score of
" << p.score;
cout << ", and is on level " << p.level << "." << endl;
}
Player player1;
PrintPlayer(player1);
player1.health -= 10;
player1.score += 25;
player1.level += 1;
PrintPlayer(player1);
Using an external function to print the status of player1 may not seem like
it was worth the effort to change the code. But when these functions
become more complex, the efficiency becomes clear.
void PrintPlayer(Player p) {
if (p.health <= 0) {
cout << "This player is dead. They died on level " <<
p.level;
cout << " with a score of " << p.score << "." << endl;
}
else {
cout << "This player has " << p.health << " health, a score
of " << p.score;
cout << ", and is on level " << p.level << "." << endl;
}
}
Now that the PrintPlayer function can return two different strings, call the
function when the player1 object has full health, and call it again when the
object has no health.
//add code below this line
Player player1;
PrintPlayer(player1);
player1.health = 0;
player1.score += 25;
player1.level += 1;
PrintPlayer(player1);
The problem with using external functions, however, is that the changes
made to objects in one function do not translate or carry over to the next
function.
#include <iostream>
using namespace std;
class Player {
public:
int health;
int score;
int level;
Player() {
health = 100;
score = 0;
level = 1;
}
};
void PrintPlayer(Player p) {
if (p.health <= 0) {
cout << "This player is dead. They died on level " <<
p.level;
cout << " with a score of " << p.score << "." << endl;
}
else {
cout << "This player has " << p.health << " health, a score
of " << p.score;
cout << ", and is on level " << p.level << "." << endl;
}
}
int main() {
Player player1;
PrintPlayer(player1);
player1.health = 0;
player1.score += 25;
player1.level += 1;
PrintPlayer(player1);
return 0;
Class Functions
Back in the Introduction to Objects module, a class was defined as “a
collection of data and the actions that can modify the data.” The
constructor can build the “collection of data”, but nothing in the class can
modify the data. Instead, external functions were used to modify the object.
However, using external functions to modify objects is not a good practice.
Here, you will be introduced to class functions, also known as class or
instance methods, that serve to modify the data within objects.
.guides/img/mutability/ExternalVsClassFunctions
There are a few notable differences between external functions and class
functions.
1. In order for external functions to work, class variables within a class
must be public. Otherwise, the function will not be able to act on the object.
This, however, is not a good practice.
1. In C++, it is a best practice to make class variables private. Doing so
prevents external functions from accidentally making undesirable changes
to the class attributes. This is why class functions are preferred when
modifying objects.
1. Everything within the class is public in the external function example. In
contrast, class attributes are private while constructors and functions are
public in the class function example.
1. To modify an object using an external function, the syntax is
Function(Object) (i.e. ChangeLevel(mario)). On the other hand, the syntax
for using a class function is Object.Function() (i.e. mario.ChangeLevel()).
class Player {
public: //public access modifer
Player() { //constructor
health = 100;
score = 0;
level = 1;
}
void PrintPlayer() { //class function
if (health <= 0) {
cout << "This player is dead. They died on level " <<
level;
cout << " with a score of " << score << "." << endl;
}
else {
cout << "This player has " << health << " health, a
score of " << score;
cout << ", and is on level " << level << "." << endl;
}
}
Player mario;
mario.PrintPlayer();
challenge
Player mario;
PrintPlayer(mario);
Player mario;
mario.PrintPlayer();
mario.ChangeHealth(25);
mario.NextLevel();
mario.PrintPlayer();
//class function
void ChangeScore(int amount) {
score += amount;
}
//main
mario.ChangeScore(123);
Why learn about external functions that modify objects when C++
has class functions?
It might seem like a waste of time to learn how to write external functions
that modify objects, but this approach builds upon concepts you have
already seen — external functions and objects. This allows you to
understand mutability without having to worry about class functions. Once
you understand how these ideas work, transforming an external function
into an class function is much simpler. External functions that modify
objects serve as an intermediary step on the way to learning about class
functions.
More Class Functions
class Meal {
private:
vector<string> drinks;
vector<string> appetizers;
vector<string> main_courses;
vector<string> desserts;
};
Next, add a class function to add a drink to the Meal object. Use the
push_back function to add an element to the vector. So
drinks.push_back(drink) adds the drink drink to the vector drinks. Then
add a class function PrintDrinks to print out all of the elements inside the
drinks vector. Class functions are public.
//add class definitions below this line
class Meal {
public:
void AddDrink(string drink) {
drinks.push_back(drink);
}
void PrintDrinks() {
for (auto a: drinks) {
cout << a << endl;
}
}
private:
vector<string> drinks;
vector<string> appetizers;
vector<string> main_courses;
vector<string> desserts;
};
Create a Meal object in main and then test your code with the following
added commands.
Meal dinner;
dinner.AddDrink("water");
dinner.PrintDrinks();
Now create the AddAppetizer class function for the class. Like the AddDrink
function above, AddAppetizer accepts a string as a parameter and adds it as
an element to the appetizers attribute (which is a vector). Then create a
PrintAppetizers function to print what’s inside the appetizers vector.
void AddDrink(string drink) {
drinks.push_back(drink);
}
void PrintDrinks() {
for (auto a: drinks) {
cout << a << endl;
}
}
void AddAppetizer(string app) {
appetizers.push_back(app);
}
void PrintAppetizers() {
for (auto a: appetizers) {
cout << a << endl;
}
}
Add "bruschetta" as an appetizer to the dinner object, then call the class
function PrintAppetizers like below.
Meal dinner;
dinner.AddDrink("water");
dinner.PrintDrinks();
dinner.AddAppetizer("bruschetta");
dinner.PrintAppetizers();
challenge
Meal code
#include <iostream>
#include <vector>
using namespace std;
class Meal {
public:
void AddDrink(string drink) {
drinks.push_back(drink);
}
void PrintDrinks() {
for (auto a: drinks) {
cout << a << endl;
}
}
void AddAppetizer(string app) {
appetizers.push_back(app);
}
void PrintAppetizers() {
for (auto a: appetizers) {
cout << a << endl;
}
}
void AddMainCourse(string mc) {
main_courses.push_back(mc);
}
void PrintMainCourses() {
for (auto a: main_courses) {
cout << a << endl;
}
}
void AddDessert(string dessert) {
desserts.push_back(dessert);
}
void PrintDesserts() {
for (auto a: desserts) {
cout << a << endl;
}
}
private:
vector<string> drinks;
vector<string> appetizers;
vector<string> main_courses;
vector<string> desserts;
};
int main() {
Meal dinner;
dinner.AddDrink("water");
dinner.PrintDrinks();
dinner.AddAppetizer("bruschetta");
dinner.PrintAppetizers();
dinner.AddMainCourse("roast chicken");
dinner.PrintMainCourses();
dinner.AddDessert("chocolate cake");
dinner.PrintDesserts();
return 0;
}
Printing the Meal
#include <iostream>
#include <vector>
using namespace std;
class Meal {
public:
void AddDrink(string drink) {
drinks.push_back(drink);
}
void PrintDrinks() {
for (auto a: drinks) {
cout << a << endl;
}
}
void AddAppetizer(string app) {
appetizers.push_back(app);
}
void PrintAppetizers() {
for (auto a: appetizers) {
cout << a << endl;
}
}
void AddMainCourse(string mc) {
main_courses.push_back(mc);
}
void PrintMainCourses() {
for (auto a: main_courses) {
cout << a << endl;
}
}
void AddDessert(string dessert) {
desserts.push_back(dessert);
}
void PrintDesserts() {
for (auto a: desserts) {
cout << a << endl;
}
}
private:
vector<string> drinks;
vector<string> appetizers;
vector<string> main_courses;
vector<string> desserts;
};
int main() {
Meal dinner;
dinner.AddDrink("water");
dinner.PrintDrinks();
dinner.AddAppetizer("bruschetta");
dinner.PrintAppetizers();
dinner.AddMainCourse("roast chicken");
dinner.PrintMainCourses();
dinner.AddDessert("chocolate cake");
dinner.PrintDesserts();
return 0;
What you want to do next is to print all of the Meal attributes that you
have in a format that is concise, clear, and makes sense. The ideal output
should also take into consideration how many items there are. Here are
some sample output:
Drinks: None
void PrintDrinks() {
if (drinks.size() == 0) {
cout << "Drinks: None" << endl;
}
else if (drinks.size() == 1) {
cout << "Drinks: " << drinks.at(0) << endl;
}
else if (drinks.size() == 2) {
cout << "Drinks: " << drinks.at(0) << " and " <<
drinks.at(1) << endl;
}
}
However, how will you select for categories that have 3 or more items? See
if you can try doing so on your own. Run your code a couple of times using
PrintDrinks after adding different amounts of elements using AddDrink.
Sample Code
void PrintDrinks() { //class definition
if (drinks.size() == 0) {
cout << "Drinks: None" << endl;
}
else if (drinks.size() == 1) {
cout << "Drinks: " << drinks.at(0) << endl;
}
else if (drinks.size() == 2) {
cout << "Drinks: " << drinks.at(0) << " and " <<
drinks.at(1) << endl;
}
else {
cout << "Drinks: ";
for (int i = 0; i < drinks.size() - 1; i++) {
cout << drinks.at(i) << ", ";
}
cout << "and " << drinks.at(drinks.size() - 1) << endl;
}
}
Meal dinner;
dinner.PrintDrinks();
dinner.AddDrink("water");
dinner.PrintDrinks();
dinner.AddDrink("lemonade");
dinner.PrintDrinks();
dinner.AddDrink("tea");
dinner.PrintDrinks();
dinner.AddDrink("coffee");
dinner.PrintDrinks();
Expected Output
Sample Code
#include <iostream>
#include <vector>
using namespace std;
class Meal {
public:
void AddDrink(string drink) {
drinks.push_back(drink);
}
void PrintDrinks() {
if (drinks.size() == 0) {
cout << "Drink(s): None" << endl;
}
else if (drinks.size() == 1) {
cout << "Drink(s): " << drinks.at(0) << endl;
}
else if (drinks.size() == 2) {
cout << "Drink(s): " << drinks.at(0) << " and " <<
drinks.at(1) << endl;
}
else {
cout << "Drink(s): ";
for (int i = 0; i < drinks.size() - 1; i++) {
cout << drinks.at(i) << ", ";
}
cout << "and " << drinks.at(drinks.size() - 1) << endl;
}
}
void AddAppetizer(string app) {
appetizers.push_back(app);
}
void PrintAppetizers() {
if (appetizers.size() == 0) {
cout << "Appetizers(s): None" << endl;
}
else if (appetizers.size() == 1) {
cout << "Appetizers(s): " << appetizers.at(0) << endl;
}
else if (appetizers.size() == 2) {
cout << "Appetizers(s): " << appetizers.at(0) << " and "
<< appetizers.at(1) << endl;
}
else {
cout << "Appetizers(s): ";
for (int i = 0; i < appetizers.size() - 1; i++) {
cout << appetizers.at(i) << ", ";
}
cout << "and " << appetizers.at(appetizers.size() - 1)
<< endl;
}
}
void AddMainCourse(string mc) {
main_courses.push_back(mc);
}
void PrintMainCourses() {
if (main_courses.size() == 0) {
cout << "Main Course(s): None" << endl;
}
else if (main_courses.size() == 1) {
cout << "Main Course(s): " << main_courses.at(0) <<
endl;
}
else if (main_courses.size() == 2) {
cout << "Main Course(s): " << main_courses.at(0) << "
and " << main_courses.at(1) << endl;
}
else {
cout << "Main Course(s): ";
for (int i = 0; i < main_courses.size() - 1; i++) {
cout << main_courses.at(i) << ", ";
}
cout << "and " << main_courses.at(main_courses.size() -
1) << endl;
}
}
void AddDessert(string dessert) {
desserts.push_back(dessert);
}
void PrintDesserts() {
if (desserts.size() == 0) {
cout << "Dessert(s): None" << endl;
}
else if (desserts.size() == 1) {
cout << "Dessert(s): " << desserts.at(0) << endl;
}
else if (desserts.size() == 2) {
cout << "Dessert(s): " << desserts.at(0) << " and " <<
desserts.at(1) << endl;
}
else {
cout << "Dessert(s): ";
for (int i = 0; i < desserts.size() - 1; i++) {
cout << desserts.at(i) << ", ";
}
cout << "and " << desserts.at(desserts.size() - 1) <<
endl;
}
}
void PrintMeal() {
PrintDrinks();
PrintAppetizers();
PrintMainCourses();
PrintDesserts();
}
private:
vector<string> drinks;
vector<string> appetizers;
vector<string> main_courses;
vector<string> desserts;
};
int main() {
Meal dinner;
dinner.AddDrink("Pepsi");
dinner.AddDrink("Sprite");
dinner.AddAppetizer("egg rolls");
dinner.AddAppetizer("pot stickers");
dinner.AddAppetizer("buffalo wings");
dinner.AddMainCourse("smoked salmon");
dinner.PrintMeal();
return 0;
}
Lab 1
Lab 1
For this lab, you will create a Student class that has the following private
class attributes:
class Student {
private:
string name;
int grade;
int score;
};
Next, let’s define the constructor Student with two parameters string n for
the student’s name and int g for the student’s grade.
class Student {
public:
Student(string n, int g) {
name = n;
grade = g;
}
private:
string name;
int grade;
int score;
};
Now, you want to define a class function called StudentStatus that takes a
student’s int score as a parameter and checks whether the score is a
passing score or not. A passing score is 65 or higher. If the score is less than
65, then the student did not pass their grade and will remain in the same
grade they are now. Otherwise, if the student has a score of 65 or higher,
then they have passed and will move on to the next grade and their grade
attribute will increase by 1. The function should also output a message
providing some context regarding whether the student has been promoted
to the next grade or not.
class Student {
public:
Student(string n, int g) {
name = n;
grade = g;
}
void StudentStatus(int s) {
if (s < 65) {
score = s;
cout << name << " has not graduated and will remain in
grade ";
cout << grade << "." << endl;
}
else {
score = s;
cout << name << " has graduated and will be promoted to
grade ";
cout << grade + 1 << "." << endl;
}
}
private:
string name;
int grade;
int score;
};
In main, try a few test cases to see if StudentStatus updates alice’s grade
level correctly.
Lab 2
It is important to understand why class attributes are labeled as private.
This provides a level of protection for your code since it does not allow the
user to interact with the class attributes directly.
class Student {
public:
Student() {
name;
grade;
}
public:
string name = "Alice";
int grade = 4;
int score = 65;
};
Because the code above has public class attributes, the following code in
main can change those attributes’ values directly.
Student steve;
steve.name = "Steve";
cout << steve.name << endl;
However, if you change the class attributes from public to private, the
code in main will no longer work.
//add class definitions below this line
class Student {
public:
Student() {
name;
grade;
}
private:
string name = "Alice";
int grade = 4;
int score = 65;
};
Student steve;
steve.name = "Steve";
cout << steve.name << endl;
class Student {
public:
Student() {
name;
grade;
}
void ChangeName(string n) {
name = n;
}
string ReturnName() {
return name;
}
private:
string name = "Alice";
int grade = 4;
int score = 65;
};
Student steve;
steve.ChangeName("Steve");
cout << steve.ReturnName() << endl;
Although using class functions may result in longer code, it prevents the
user from seeing and interacting with the class attributes directly. This is
why using class attributes is a best practice.
Lab Challenge
Copy and paste the Zoo class below into the text editor.
class Zoo {
public:
Zoo(int bc, int p, int r, int b) {
big_cats = bc;
primates = p;
reptiles = r;
birds = b;
}
private:
int big_cats; //for "big cats"
int primates; //for "primates"
int reptiles; //for reptiles
int birds; //for birds
};
DO NOT CHANGE the existing code in main or you will not pass the test:
250
40
birds
324
168
big cats