0% found this document useful (0 votes)
13 views

PART 3

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
13 views

PART 3

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 131

Learning Objectives: Function

Basics
Demonstrate how to define a function

Differentiate between the function header and the


function body

Identify the importance of the order of function


definitions

Identify the purpose of C++ code documentation


Function Definition

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

The function header may contain several keywords to determine the


function type. Next is the name of the function. “Ordinarily, functions
should start with a capital letter and have a capital letter for each new
word.”
(https://google.github.io/styleguide/cppguide.html#Function_Names). Some
examples include: GreetTwice, AddThree, FindArea, etc. Parentheses are
required but the parameter(s) within them are not. Any command(s)
associated with the function should be indented as a best practice and
enclosed within curly braces {}. This command(s) comprises the body of
the function.

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;
}

You’ll notice that an error is produced. This happens because when


running C++ programs, a main function is required. Let’s go ahead and add
int main() to the code like so.

#include <iostream>
using namespace std;

void GreetTwice() {
cout << "Hello" << endl;
cout << "Hello" << endl;
}

int main() {

Nothing is outputted when the program is executed. This happens because


creating a function alone does not cause C++ to run it. You have to explicitly
call the function if you want it to run. Functions are usually called within
the main function. To call a function, simply start with its name, provide any
parameters if needed, and end with a semicolon.
#include <iostream>
using namespace std;

void GreetTwice() {
cout << "Hello" << endl;
cout << "Hello" << endl;
}

int main() {
GreetTwice();
}

challenge

What happens if you:


Call GreetTwice again by adding another GreetTwice(); to the
main() function?
Modify GreetTwice by adding the line cout << "Goodbye" << endl;
underneath the second cout << "Hello" << endl;?
Add the command return 0 as the last line in the main() function?

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.

Order of Function Definitions


The order of function definitions is important in C++. If the code is changed
to the following, what do you think will be outputted?

#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.

Order of Different Functions


The order of the functions themselves also matter. What happens if you
swap the position of main() with GreetTwice() and vice versa?

#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

C++ Code Documentation


Including C++ code documentation prior to function definitions is standard.
Doing so enables users to gain clarity on the purpose of the function, any
parameters that are used, and what the function returns. Users can also see
who wrote the function as well as what version the function is on. There
are several ways to document C++ code, however the style that we will be
using for this module models that of Java’s (also known as Javadoc). Here is
an example, also present within the text editor to your left:

/**
* 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

Identify which value is assigned to each parameter

Catch exceptions with a try, throw, and catch

Define functions with the same name but different


parameters

Demonstrate how to declare a function with an array


and vector as a parameter
Passing 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

What happens if you:


Change the function call in main() to Add(5, "seven");?
Change the function call to Add(); without any parameters?
Change the function call to Add(5, 10, 15);?
Change the entire function to…

void Add(int num1, int num2, int num3) {


cout << num1 + num2 + num3 << endl;
}

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

What happens if you:


Change the function call in main() to AddSub(10, 15, 5);?
Change the function call in main() to AddSub(15, 5, 10);?
Change the function call in main() to AddSub(10 + 5, 20 / 4, 5 *
2);?
Checking Parameters

Checking Parameter Usage


Copy and paste the code below into the text editor and then TRY IT.

/**
* 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;
}

You’ll notice that the code produces an exception. An exception occurs


when an operation cannot be successfully completed because a rule is
broken. For example, dividing by 0 results in an undefined answer. Thus
when you try to divide 5 by 0, you get an exception as a response. Not all
exception messages are created equal. Some are more clear than others.
However, you may choose to clearly define an exception by using a try,
throw, and catch.

.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

What happens if you:


Change the function call to Divide(5, 2);?
Change the function call to Divide(0, 2);?
Change the function call to Divide(14.5, 2);?
Change the function call to Divide(14.5, "2");?
important

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.

runtime_error() is one example of an exception that can be used to


produce a specified error message. e is the variable name for which
you are calling the exception by. In C++, exceptions are caught by
reference, not value. Thus, runtime_error& e is preferred over
runtime_error e.

For a list of other exceptions, visit: C++ Exceptions.


Parameter Types

Function with Different Parameters


In C++, you are allowed to define functions with the same name as long as
the parameters are different in quantity or type. Copy the code below and
TRY IT.

/**
* 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

What happens if you:


Change the function call to Add(3, 14, 9);?
Change the function call to Add(3, 14, 9, 2);?

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

What happens if you:


Change the function call to Add(3, "14");?
Change the function call to Add("14", 3);?
Alternative Parameters

Alternative Parameter Types


Function parameters do not necessarily need to belong to one of the four
commonly used data types (int, string, double, bool). In fact, parameters
can be arrays/vectors and even objects. For now, we will not focus on
objects, which will be covered in a future module.

/**
* 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

What happens if you:


Change string names[] = {"Alan", "Bob", "Carol"}; to string
names[3];?
Add names[0] = "Alan"; to the line below string names[3];?
Change the first function parameter of string array[] to string*
array?

Why Doesn’t the Code Below Work?


/**
* This function prints all values of an array
*
* @param array A string array
*/
void PrintArray(string array[]) {
for (int i = 0; i < sizeof(array) / sizeof(array[0]); i++) {
cout << array[i] << endl;
}
}

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

Explain what the keyword “const” does


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

What happens if you:


Change MyFunction2 to look like this:

void MyFunction2() {
string my_var2 = "Hello";
cout << my_var2 << endl;
}

More Local Scope


Each function has its own local scope. That means you can declare two
variables with the same name as long as they are in separate functions. The
blue my_var exists only in the light blue box, and the red my_var exists only
in the light red box. The boundaries of local scope keep C++ from
overwriting the value of the first variable with the contents of the second.
.guides/img/LocalScope2

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

What happens if you:


Declare MyFunction3() as:

void MyFunction3() {
string my_var = "Hola";
cout << my_var << endl;
}

and call it by including MyFunction3(); within the main() function.


Global Scope

Global Scope - Referencing Variables


When a variable is declared inside a function, it has local scope. When a
variable is declared outside of all existing functions, it has global scope.
Since global variables are declared outside of functions, they can be
referenced inside any function. Look at the image below. The yellow block
holds everything within the program including the variable greeting. This
enables all functions within the program to access that variable since it is
considered to be global. Copy and paste the code below and then click TRY
IT.

.guides/img/GlobalScope

string greeting = "Hello"; //global variable

void SayHello() {
cout << greeting << endl; //can access global variable
greeting
}

int main() {
SayHello();
return 0;
}

Global Scope - Modifying Variables


Once a global variable becomes available, a function can modify the
content of that variable as needed.

string greeting = "Hello";

void SayHello() {
greeting = "Bonjour";
cout << greeting << endl;
}

int main() {
SayHello();
return 0;
}

challenge

What happens if you:


Change greeting = "Bonjour"; within SayHello() to greeting =
"Hola";?
Change the entire program to:

string greeting = "Hello";

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

Global vs. Local Scope


If a variable is declared and initialized both locally and globally, that
variable will retain its content depending on how it is used. In the example
below, my_var is declared and initialized globally as global scope and
locally as local scope. Since the variable has differing scopes, it retains its
value when called or printed.

string my_var = "global scope";

void PrintScope() {
string my_var = "local scope";
cout << my_var << endl;
}

int main() {
PrintScope();
cout << my_var << endl;
}

The exception to this rule is when a function modifies a global variable. In


such a case, the content of the global variable is changed.

string my_var = "global scope";

void PrintScope() {
my_var = "local scope";
cout << my_var <<endl;
}

int main() {
PrintScope();
cout << my_var << endl;
}
challenge

What happens if you:


Change the code to:

string my_var = "global scope";

void PrintScope(string my_var) {


my_var = "local scope";
cout << my_var << endl;
}

int main() {
PrintScope(my_var);
cout << my_var << endl;
}

When a global variable is also a function parameter, it is considered to be


the same as if the function declared and initialized its own local variable.
In this case, the variable has both a local and global scope and will retain
its value depending on its scope.

The “const” Keyword


If you want a global variable to remain unchanged throughout the
program, you can declare the variable as const. const variables are also
referred to as constants. Constants never change and are “named with a
leading ‘k’ followed by mixed case. Underscores can be used as separators
in the rare cases where capitalization cannot be used for separation.”
Source: Google

Another common way to label constants is to use names in all uppercase


with words separated by an underscore (_). For example: MY_CONSTANT.
const string kMyConstant = "I NEVER CHANGE";

void PrintScope() {
kMyConstant = "I CAN'T CHANGE";
cout << kMyConstant << endl;
}

int main() {
PrintScope();
cout << kMyConstant << endl;
}

challenge

What happens if you:


Remove the keyword const from the code?
Learning Objectives: Returning
Values
Use the return keyword to return a value

Identify the return value of the print statement

Demonstrate the ability to return several different data


types

Create and apply helper functions


Returning Values

The Return Keyword


Instead of just printing data, functions can also return data. Think of the
sizeof() and length() functions. They help return the size or length (in
integer) of an array and string respectively. So the return value of these
functions is of type int. Both sizeof() and length() do not print anything
to the screen, they just return a number. From here on out, user-defined
functions will avoid just printing to the screen. Instead, they will return a
value. To return a value, simply use the return keyword.

/**
* 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

What happens if you:


Remove all lines of code within main() and replace them with just
cout << AddFive(10) << endl; and then return 0;?

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);
}

int main() { //int function


cout << ReturnInt(1, 2) <<endl;
cout << ReturnDouble(1, 2) <<endl;
cout << ReturnString("1", "2") << endl;
return 0;
}

challenge

Can you write a function that returns a


vector?
If you want to return a vector, one possible approach is to have a
vector be passed as a parameter. You can then modify the vector in
some way, and then return it to the system.
Sample Code

The code below takes a vector of numbers as a parameter for the


function MultiplyFive(). The function creates a new empty vector,
multiplies each element of the parameter vector by 5, and then adds
those new products to the new vector. Finally, the new vector is
returned. To print the returned vector, use another enhanced for loop
to iterate through the vector after it has been initialized.

vector<int> MultiplyFive(vector<int>& my_list) {


vector<int> new_list;
for (auto a : my_list) {
new_list.push_back(a * 5);
}
return new_list;
}

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);

vector<int> print_list = MultiplyFive(numbers);


for (auto a : print_list) {
cout << a << endl;
}

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:

It would be quite easy to write a C++ function to calculate the area of a


circle. However, instead of knowing the radius of the circle, you have the
X/Y coordinates for a point at the center of the circle and another point on
the circle. The distance formula (which is based on the Pythagorean
Theorem) can calculate the radius of the circle.

.guides/img/Radius

The FindRadius() function uses the distance formula to calculate the


distance between 2 pairs of points. The FindArea() function finds the area
of a circle by relying on the FindRadius() function. Therefore, the
FindRadius() function is a helper function. Helper functions help shorten
how much code is needed to accomplish certain tasks.

/**
* 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

The Slope Formula


You are going to write a function that takes in 4 doubles as parameters.
These parameters represent two sets of coordinate points labeled as x1, y1,
x2, and y2. The function will then use these points to calculate the slope that
their line creates and then prints that slope to the user.

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.

void GetSlope(double x1, double y1, double x2, double y2) {


}

Printing the Slope


The final step is to print the slope, but we’ll need the slope formula in order
to do that. The slope formula is defined as (y2 - y1) / (x2 - x1).

cout << (y2 - y1) / (x2 - x1) << endl;

Testing the Function


In order to use a function, you’ll need to call it by specifying its name
within the main() function. Note that the function requires parameters so
we’ll need to provide some arguments in order for the function to work
properly. Let’s use the points (3, 2) and (5, 6) as our coordinates which
correspond to (x1, y1) and (x2, y2) respectively. Lastly, it is a best
practice to include return 0 as the last statement inside main().

int main() {
GetSlope(3, 2, 5, 6);
return 0;
}

The GetSlope() function will apply the slope formula (6 - 2) / (5 - 3)


and print the result 2.0 to the user. Make sure to also include
documentation so that other users can understand your function.

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

Local and Global Variables


For this lab, we are going to be adding local and global variables to our
previously created GetSlope() function. Remember that global variables
exist outside of functions while local variables exist inside functions.
Depending on how you declare your local and global variables, they will
behave differently per situation.

Global Variables
First, let’s add some global variables to our program.

double input1;
double input2;
double input3;
double input4;

The GetSlope() Function


As from before, the function will still take 4 doubles as parameters named
x1, y1, x2, and y2. However, we’re going to implement two different
calculations within the function. Specifically, we are going to calculate the
difference between the y coordinates first, then calculate the difference
between the x coordinates. These calculations will then be assigned to local
variables called y_change and x_change. Finally, the function will print the
quotient between y_change and x_change, which is also the slope itself.

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;
}

Testing the Function


To make things more dynamic, we’ll actually make use of a cin within the
main() function. cin will take in inputs from the user and assign them to
our 4 global variables input1, input2, input3, and input4. These inputs will
later correspond to x1, y1, x2, and y2. Having cin enables the user to decide
what the coordinate points will be.

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;

GetSlope(input1, input2, input3, 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;

GetSlope(input1, input2, input3, 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.

double GetSlope(double x1, double y1,


double x2, double y2) { //replace void with
double
double y_change = y2 - y1;
double x_change = x2 - x1;
return y_change / x_change; //returns a double
}

Modifying the Return Value


Notice that our function returns a single double, which is nice but not
extremely helpful when it comes to determining rise and run for slopes
(rise / run). Let’s say we want instead to express the slope in the rise /
run format. rise is the change in y values and run is the change in x values.
Unfortunately, we can’t simply do return y_change + " / " + x_change.
Why? Because the " / " is a string which is not compatible with the
current return value of double. One way around this is to convert the
doubles into strings. Doing so will force us to change our double function
into a string function.

string GetSlope(double x1, double y1,


double x2, double y2) { //replace double with
string
double y_change = y2 - y1;
double x_change = x2 - x1;
return to_string(y_change) + " / " + to_string(x_change);
//returns a string
}

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;

GetSlope(input1, input2, input3, input4);


}

Printing the Slope


If we try to run the program, we will not see anything printed to the screen.
Why? Because there is no print statement anywhere within the code. All
the program does is calculate and return values. Returning values and
printing them are not the same thing. Therefore, we need to include a print
statement if we want to actually see the output. However, we cannot just
include a print statement within our function because it is a string
function, not a void one. Fortunately, we can use our main() function to
print our desired output.

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;

cout << GetSlope(input1, input2, input3, input4) << endl;


//prints what is returned by the GetSlope() function
}
Lab: Helper Functions

Purpose of Helper Functions


When a function calls another function, it is using that function to help it
perform a particular action. Helper functions provide users with more
flexibility when it comes to developing functions. Additionally, helper
functions help reduce code repetition because the same action only has to
be written once. Let’s start by creating a few helper functions that will help
us with other functions later.

/**
* 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.

Using Helper Functions


/**
* 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)) + " / " +
to_string(GetRun(x1, x2));
//need to convert doubles to strings!
}

Notice how within the two functions above GetSlopeDecimal() and


GetSlopeFraction(), the previous helper functions GetRise() and GetRun()
are called. Having 4 functions at our disposal provides us with a flexibility
that a single function cannot offer. In this program, we can get the slope in
its decimal form and its fraction form in addition to the rise and run
individually. If we wanted, we can continue to build more into this
program.

Complete and Run the Program


Copy over the rest of the program and then test it.

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;

cout << "Rise: ";


cout << GetRise(input2, input4) << endl;
cout << "Run: ";
cout << GetRun(input1, input3) << endl;
cout << "Calculated form: ";
cout << GetSlopeDecimal(input1, input2, input3, input4) <<
endl;
cout << "Slope form: ";
cout << GetSlopeFraction(input1, input2, input3, input4) <<
endl;
}
Lab Challenge: Greeting Machine

Create a Greeting Machine


You are going to develop a function that takes a vector of strings as a
parameter, iterates through that vector and greets every element in it with
"Hello" followed by a newline.

Existing Code:

#include <iostream>
#include <vector>
using namespace std;

//add code below this line

//add code above this line

int main(int argc, char** argv) {


vector<string> names;
for (int i = 1; i < argc; i++) {
names.push_back(argv[i]);
}
SayHello(names);
}

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.

Compile and test your code with a few different values


create

Test with Alan & Bob


Expected Output
Hello Alan
Hello Bob

create

Test with 1, 2 & 3


Expected Output
Hello 1
Hello 2
Hello 3

create

Test with Codio


Expected Output
Hello Codio
Learning Objectives: Recursion
Define recursion

Identify the base case

Identify the recursive pattern


What is Recursion

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.

Because 5! is self-similar, recursion can be used to calculate the answer.


Recursive functions are functions that call themselves. Copy the following
code into the text editor on your left and click TRY IT to test the code.
#include <iostream>
using namespace std;

//add function definitions below this line

/**
* 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);
}
}

//add function definitions above this line

int main() {

//add code below this line

cout << Factorial(5) << endl;


return 0;

//add code above this line

Optional: Click the Code Visualizer link below to see how C++ handles this
recursive function behind-the-scenes.

Code Visualizer

Recursion is an abstract and difficult topic, so it might be a bit hard to


follow what is going on here. When n is 5, C++ starts a multiplication
problem of 5 * Factorial(4). The function runs again and the
multiplication problem becomes 5 * 4 * Factorial(3). This continues
until n is 1. C++ returns the value 1, and C++ solves the multiplication
problem 5 * 4 * 3 * 2 * 1. The video below should help explain how 5! is
calculated recursively.
Error

The Base Case


Each recursive function has two parts: the recursive case (where the
function calls itself with a different parameter) and the base case (where
the function stops calling itself and returns a value).

.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

What happens if you:


Change the Factorial() function to look like:

int Factorial(int n) {
if (n == 1) { //base case
return 1;
}
else { //recursive case
return n * Factorial(n - 1);
}
}

Change the print statement to cout << Factorial(0) << endl;?

Try to modify the base case so that Factorial(0) does not result in
an error.

Test your new base case with a negative number.

Solution

The factorial operation only works with positive integers. So the


base case should be:

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

Calculating a Fibonacci number is self-similar, which means it can be


defined with recursion. Setting the base case is important to avoid infinite
recursion. When the number n is 0 the Fibonacci number is 0, and when n
is 1 the Fibonacci number is 1. So if n is less than or equal to 1, then return
n. That is the base case.

/**
* @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

What happens if you:


Change the print statement to cout << Fibonacci(0) << endl;?
Change the print statement to cout << Fibonacci(8) << endl;?
Change the print statement to cout << Fibonacci(30) << endl;?

Where is the code visualizer?


The code visualizer will only step through your code 1,000 times. These
recursive functions exceed this limit and generate an error message.
Because of this, the code visualizer was removed.

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

What happens if you:


Change fibonacci_length to 10?
Change fibonacci_length to 30?
Change fibonacci_length to 50?

Why is C++ timing out?


The code written above is terribly inefficient. Each time through the loop,
C++ is calculating the same Fibonacci numbers again and again. When i is
1, C++ calculates the Fibonacci numbers for 0 and 1. When i is 2, C++ is
calculating the Fibonacci numbers for 0, 1, and 2. Once i becomes large
enough, it becomes too much work for C++ to have to recalculate these
large numbers over and over again. There is a more efficient way to do this
by using vector. The idea is to store previously calculated Fibonacci
numbers in the vector. So instead of recalculating the same numbers again
and again, you can get these numbers from the vector. If a Fibonacci
number is not in the vector, then calculate it and add it to the vector. Copy
and paste the code below into the IDE if you want to run it.

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

Lab 1 - Recursive Tree

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.

Turtle Graphics Review


We will be using Turtle Graphics to visualize recursion. Here is some
review on what a Turtle object (tina) can do in C++:

Turtle Graphics Review

tina.forward(n) - Where n represents the number of pixels.


tina.backward(n) - Where n represents the number of pixels.
tina.right(d) - Where d represents the number of degrees.
tina.left(d) - Where d represents the number of degrees.
tina.pencolor({“COLOR”}) - Where COLOR represents the track or line
color you want tina to leave behind.
tina.width(W) - Where W represents how wide (in pixels) tina’s track is.
tina.shape(“SHAPE”) - Where SHAPE represents the shape tina takes.
tina.speed(SPEED) - Where SPEED represents how fast tina moves
Command Parameter Examples
Where COLOR
represents the
red, orange, yellow,
tina.pencolor({"COLOR"}) track or line color
green, blue, purple
you want tina to
leave behind
Where W
represents how any positive integer
tina.width(W)
wide (in pixels) (e.g. 1, 10, 123, etc.)
tina’s track is
Where SHAPE
triangle, indented
tina.shape("SHAPE") represents the
triangle, square, arrow
shape tina takes
TS_FASTEST, TS_FAST,
Where SPEED
TS_NORMAL,
tina.speed(SPEED) represents how
TS_SLOW,
fast tina moves
TS_SLOWEST

And some instructions on how Turtle Graphics work on the Terminal:

.guides/img/TurtleGraphicsFlow

1. TRY IT button is clicked by the user.


2. The Terminal tab is opened.
3. The terminal runs the command to compile the program and to display
the graphical output.
4. The output is displayed as a screen on the bottom left panel. You can
click the screen to close the output.
5. Click on the file name tab to go back to the text editor if you want to
make changes to the program.

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).

This is the code you have so far in the text editor:

////////// DO NOT EDIT HEADER! //////////


#include <iostream> //
#include "CTurtle.hpp" //
#include "CImg.h" //
using namespace cturtle; //
using namespace std; //
/////////////////////////////////////////

/**
* @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) {

//add function definitions below

//add function definitions above

int main(int argc, char** argv) {

//add code below this line

//add code above this line

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.

void RecursiveTree(int branch_length, int angle, Turtle& t) {

//add function definitions below

if (branch_length > 5) {

//add function definitions above

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.

void RecursiveTree(int branch_length, int angle, Turtle& t) {

//add function definitions below

if (branch_length > 5) {
t.forward(branch_length);
t.right(angle);
RecursiveTree(branch_length - 15, angle, t);
}

//add function definitions above

In main(), let’s call the RecursiveTree() function and pass in some initial
values.
int main(int argc, char** argv) {

//add code below this line

TurtleScreen screen(400, 300);


Turtle tina(screen);
RecursiveTree(45, 20, tina);

//add code above this line

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.

void RecursiveTree(int branch_length, int angle, Turtle& t) {

//add function definitions below

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);
}

//add function definitions above

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) {

//add function definitions below

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);
}

//add function definitions above

challenge

What happens if you:


Increase the branch length argument when calling RecursiveTree()
in main() for the first time such as 35 or 60?
Increase and decrease the angle argument when calling
RecursiveTree() in main() for the first time such as 10 or 40?
Change all of the recursive cases in the function from
branch_length - 15 to something smaller like branch_length - 5?
Change the base case to if (branchLength > 1) in the function?
Rotate the tina 90 degrees to the left before calling RecursiveTree()
in main() for the first time?

Code Sample

////////// DO NOT EDIT HEADER! //////////


#include <iostream> //
#include "CTurtle.hpp" //
#include "CImg.h" //
using namespace cturtle; //
using namespace std; //
/////////////////////////////////////////

/**
* @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) {

//add function definitions below

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);
}

//add function definitions above

int main(int argc, char** argv) {

//add code below this line

TurtleScreen screen(400, 300);


Turtle tina(screen);
tina.left(90); //rotates Turtle's original position
tina.speed(TS_FASTEST); //speeds up Turtle's movement
RecursiveTree(35, 20, tina);

//add code above this line

screen.exitonclick();
return 0;

}
Lab 2

Lab 2 - The Hilbert Curve

Hilbert Curve

The Hilbert Curve is a fractal, space-filling curve. Start by creating a Turtle


object, and then write the function header for the recursive function
Hilbert. The parameters for the function are the distance the turtle will
travel, the rule to be used, an angle (determines how tight the fractal is),
depth (how intricate the fractal is), and the Turtle object.

This is the code you have so far in the text editor:


////////// DO NOT EDIT HEADER! //////////
#include <iostream> //
#include "CTurtle.hpp" //
#include "CImg.h" //
using namespace cturtle; //
using namespace std; //
/////////////////////////////////////////

/**
* @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) {

//add function definitions below

//add function definitions above

int main(int argc, char** argv) {

//add code below this line

TurtleScreen screen(400, 300);


Turtle tina(screen);

//add code above this line

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) {

//add function definitions below

if (depth > 0) {
if (rule == 1) {
//rule1 code
}
if (rule == 2) {
//rule2 code
}
}

//add function definitions above

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.

int main(int argc, char** argv) {

//add code below this line

TurtleScreen screen(400, 300);


Turtle tina(screen);
Hilbert(5, 1, 90, 5, tina);

//add code above this line

screen.exitonclick();
return 0;

Speeding up the turtle


The Hilbert Curve can be slow to draw. You can change the speed of the
Turtle tina with the following command tina.speed(TS_FASTEST); before
calling the Hilbert() function.
challenge

What happens if you:


Change the dist parameter to 3?
Start with the rule parameter as 2?
Change the angle parameter to 85?
Change the depth parameter to 4?

Note: You might need to adjust your TurtleScreen’s size to capture all
of the output.

Sample Code

////////// DO NOT EDIT HEADER! //////////


#include <iostream> //
#include "CTurtle.hpp" //
#include "CImg.h" //
using namespace cturtle; //
using namespace std; //
/////////////////////////////////////////

/**
* @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) {

//add function definitions below

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);
}
}

//add function definitions below

int main(int argc, char** argv) {

//add code below this line

TurtleScreen screen(400, 300);


Turtle tina(screen);
tina.speed(TS_FASTEST);
Hilbert(8, 1, 90, 4, tina);

//add code above this line

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;

//add function definitions below this line

//add function definitions above this line

int main(int argc, char** argv) {


cout << RecursivePower(stoi(argv[1]), stoi(argv[2])) << endl;
return 0;
}

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.

Compile and test your code with a few different values

Expected Output
216

Expected Output
1
Learning Objectives: Classes and
Objects

Define the terms class, objects, instance, and instantiate

Identify the difference between classes and objects

Create a user-defined object

Modify an object’s attribute with dot notation

Define a constructor

Create a copy of an object


Built-In Objects

The String Object and Others


You have already been using built-in C++ objects. Strings are an example of
a C++ object.

string s = "I am a string";


cout << "s is a: " << typeid(s).name() << endl;

challenge

Try these variations:


Explore some of the functions associated with the string class.
* Add the line of code cout << boolalpha << s.empty() << endl;
* Add the line of code cout << s.length() << endl;
* Add the line of code s.push_back('s'); and then cout << s << endl;

C++ says that the class or type of s is


NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE (which is a
string). Replace the exiting code with the one below and run the program
again.

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.

Objects - Objects are constructed according to the blueprint that is the


class. In the code above, the variable s is a string object. It is not the class.
The string class tells C++ that s has functions like length, append, and
replace. When a programmer wants to use a class, they create an object.

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.

Instantiation - Instantiation is the process where an object is created


according to a blueprint of the class. The phrase “define a variable” means
to create a variable. The variable is given a name and a value. Once it has
been defined, you can use the variable. With objects, you use the phrase
“instantiate an object”. That means to create an object, give it a name, store
any data, and define the actions the object can perform.

.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.

//add class definitions below this line

class Actor {

};

//add class definitions above this line

Naming classes

The convention for naming classes in C++ is to use a capital letter. A


lowercase letter will not cause an error message, but it is not considered to
be “correct”. If a class has a name with multiple words, all of the words are
pushed together, and a capital letter is used for the first letter of each word.
This is called camel case.

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.

//add code below this line

Actor helen;

//add code above this line

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.

//add class definitions below this line

class Actor {
public:
string first_name;
string last_name;
};

//add class definitions above this line

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.

//add code below this line

Actor helen;
helen.first_name = "Helen";
helen.last_name = "Mirren";
cout << helen.first_name + ' ' + helen.last_name << endl;

//add code above this line


challenge

Try these variations:


Add the attribute int total_films; to the class and then assigned
helen.total_films = 80; in main.
Add the print statement cout << helen.total_films << endl; in
main.
Add the print statement cout << helen << 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

Too Much Code


Imagine that the Actor class has more attributes than on the previous page.

//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;
};

//add class definitions above this line

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.

//add code below this line

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;

//add code above this line

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.

//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;

Actor() {
first_name = "Helen";
last_name = "Mirren";
birthday = "July 26";
total_films = 80;
oscar_nominations = 4;
oscar_wins = 1;
}
};

//add class definitions above this line

Instantiating helen as an instance of the Actor class automatically calls the


constructor. Since the instance variables (attributes) have values, you can
remove those lines of code from the main function.

//add code below this line

Actor helen;
cout << helen.first_name + ' ' + helen.last_name << endl;

//add code above this line


challenge

Try these variations:


Add this print statement to the main function:

cout << helen.first_name + ' ' + helen.last_name + "\'s


birthday is " + helen.birthday + '.' << endl;

Add this print statement to the main function:

cout << helen.first_name + ' ' + helen.last_name + " won " +


helen.oscar_wins + " Oscar(s) out of " +
helen.oscar_nominations + " nomination(s)." << endl;

Change all of the + above to <<.

When you attempted to print cout << helen.first_name + ' ' +


helen.last_name + " won " + helen.oscar_wins + " Oscar out of " +
helen.oscar_nominations + " nominations" << endl;, you likely received a
very long error message. This happened because the operator + only works
with same-type objects or data. In the print statement above, you tried to
combine strings with integers which C++ did not like and therefore
complained. To solve this, simply change the + to <<.
The Constructor and Parameters

The Constructor and Parameters


Now imagine that you want to use the Actor class to instantiate an object
for Helen Mirren and Tom Hanks. Create the Actor class just as before.

//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;

Actor() {
first_name = "Helen";
last_name = "Mirren";
birthday = "July 26";
total_films = 80;
oscar_nominations = 4;
oscar_wins = 1;
}
};

//add class definitions above this line

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.

//add code below this line

Actor helen;
Actor tom;
cout << helen.first_name << ' ' << helen.last_name << endl;
cout << tom.first_name << ' ' << tom.last_name << endl;

//add code above this line


The constructor Actor class only creates an object with information about
Helen Mirren. You can make the Actor class more flexible by passing it an
argument for each of attributes in the constructor. Parameters for the
constructor function work just as they do for user-defined functions, be
sure to indicate the data type for each parameter.

//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;

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;
}
};

//add class definitions above this line

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.

//add code below this line

Actor helen("Helen", "Mirren", "July 26", 80, 4, 1);


Actor tom("Tom", "Hanks", "July 9", 76, 5, 2);
cout << helen.first_name << ' ' << helen.last_name << endl;
cout << tom.first_name << ' ' << tom.last_name << endl;

//add code above this line


challenge

Try these variations:


Create an instance of the Actor class for Denzel Washington
(December 28, 47 films, 8 nominations, 2 wins).
Print the birthday and total_films attributes for the newly created
object.

Code

Your code for the object representing Denzel Washington should look
something like this:

//add code below this line

Actor denzel("Denzel", "Washington", "December 28",


47, 8, 2);
cout << denzel.birthday << endl;
cout << denzel.total_films << endl;

//add code above this line

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;

Actor(string fn, string ln, string bd, int tf) {


first_name = fn;
last_name = ln;
birthday = bd;
total_films = tf;
oscar_nominations = 0;
oscar_wins = 0;
}
};

//add class definitions above this line

You can update the attributes once the object has been instantiated if need
be.

//add code below this line

Actor helen("Helen", "Mirren", "July 26", 80);


cout << helen.oscar_nominations << endl;
cout << helen.oscar_wins << endl;

helen.oscar_nominations = 4;
helen.oscar_wins = 1;

cout << helen.oscar_nominations << endl;


cout << helen.oscar_wins << endl;

//add code above this line


Copying Objects

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?

//add class definitions below this line

class ComicBookCharacter {
public:
string name;
int age;
string type;
};

//add class definitions above this line

int main() {

//add code below this line

ComicBookCharacter a;
a.name = "Calvin";
a.age = 6;
a.type = "human";

ComicBookCharacter b = a;
a.name = "Hobbes";

cout << "Object a name: " << a.name << endl;


cout << "Object a age: " << a.age << endl;
cout << "Object a type: " << a.type << endl;
cout << "Object b name: " << b.name << endl;
cout << "Object b age: " << b.age << endl;
cout << "Object b type: " << b.type << endl;

//add code above this line

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

Try these variations:


Add b.name = "Snoopy"; before the print statements.
Add b.type = "dog"; before the print statements.
Lab 1

Lab 1: Defining Your Own Object


When defining an object, there are a few criteria that must be met in order
for an object to be successfully created.

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;
};

Creating the Object


Once the class has been established, we can create the object by calling on
the object’s class and giving the object a name in the main function.

Student andy;
andy.name = "Andy";
andy.ID = 123456;
andy.major = "Computer Science";
andy.GPA = 3.45;
andy.age = 22;
andy.YOG = 2021;

Printing the Object’s Attributes


It is not sufficient to simply say cout << andy; in order to print the
attributes associated with andy. Instead, we must use dot notation to
specify what attribute of andy we want to output. To print andy’s ID for
example, use cout << andy.ID;. Or to print andy’s major, use cout <<
andy.major;. Add the following to the existing code and click the TRY IT
button to see what is printed about andy.

cout << andy.name << " is " << andy.age;


cout << " years old and is graduating with a degree in ";
cout << andy.major << " in " << andy.YOG << ".";
Lab 2

Lab 2: Building a Constructor


Defining an object and then having to use dot notation to assign values to it
every time can be a long and difficult task. To help alleviate that issue, we
can build a constructor to help us create an object with specified
attributes.

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;
}
};

//add class definitions above this line

int main() {

//add code below this line

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;

//add code above this line

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.

To make the constructor more flexible, we can give it parameters. A


constructor with parameters works similarly to a user-defined function in
which you provide the parameter types and the user simply has to provide
the arguments.
//add class definitions below this line

class Student {
public:
string name;
int ID;
string major;
double GPA;
int age;
int YOG;

Student(string n, int id, string m, double g, int a, int y) {


name = n;
ID = id;
major = m;
GPA = g;
age = a;
YOG = y;
}
};

//add class definitions above this line

int main() {

//add code below this line

Student andy("Andy", 123456, "Computer Science", 3.45, 22,


2021);
Student mary("Mary", 456789, "Mathematics", 3.78, 21, 2022);

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;

//add code above this line

return 0;

A constructor with parameters enables the user to decide what attributes to


assign right when the object is created. The user just has to provide those
attributes as arguments in parentheses (i.e. Student mary("Mary", 456789,
"Mathematics", 3.78, 21, 2022);).
Lab Challenge

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.

Marceline German Shepherd


Cajun Belgian Malinois
false

DO NOT CHANGE the existing code in main.

Dog dog1("Marceline", "German Shepherd");


Dog dog2 = dog1;
dog2.name = "Cajun";
dog2.breed = "Belgian Malinois";

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

Define the term mutable

Construct an external function to modify class variables


(attributes)
Mutability and External Functions

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.

//add class definitions below this line

class Player {
public:
int health;
int score;
int level;

Player() {
health = 100;
score = 0;
level = 1;
}
};

//add class definitions above this line

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;

//add code above this line

challenge

Try these variations:


Change the health of player1 to 0.
Print the status of player1 specifying that their health is 0 and the
message Game over..
One Possible Solution

player1.health = 0;
cout << "This player has " << player1.health << " health.
Game over." << endl;

External Functions and Objects


One of the benefits of functions is code reusability. The example above has
a repetition of the cout statement. This is a good opportunity to use a
function.

//add function definitions below this line

void PrintPlayer(Player p) {
cout << "This player has " << p.health << " health, a score of
" << p.score;
cout << ", and is on level " << p.level << "." << endl;
}

//add function definitions above this line


In the main function, replace the strings inside the print statements with a
call to the PrintPlayer function. Don’t forget to pass the player1 object to
the PrintPlayer function.

//add code below this line

Player player1;
PrintPlayer(player1);
player1.health -= 10;
player1.score += 25;
player1.level += 1;
PrintPlayer(player1);

//add code above this line

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.

//add function definitions below this line

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;
}
}

//add function definitions above this line

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);

//add code above this line

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;

//add class definitions below this line

class Player {
public:
int health;
int score;
int level;

Player() {
health = 100;
score = 0;
level = 1;
}
};

//add class definitions above this line

//add function definitions below this line

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;
}
}

void ChangeHealth(Player p, int amount) {


p.health += amount;
cout << "New health = " << p.health << endl;
}

//add function definitions above this line

int main() {

//add code below this line

Player player1;
PrintPlayer(player1);
player1.health = 0;
player1.score += 25;
player1.level += 1;
PrintPlayer(player1);

ChangeHealth(player1, 20); //changes health of player1


PrintPlayer(player1); //does not register changes from
ChangeHealth function

//add code above this line

return 0;

You’ll notice above that ChangeHealth(player1, 20) had no effect on


PrintPlayer(player1). player1’s health changed to 20 but after it leaves the
function, it returned to 0. Changes that occur within external functions are
not permanent. Next, you will be introduced to class functions which will
enable you to make changes to objects that are more permanent.
Learning Objectives: Class
Functions

Define the term class function

Convert an external function that modifies an object


into a class function

Demonstrate the syntax for defining and calling a class


function
External Functions vs. Class
Functions

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.

Think of a class function as a function that is attached to an object. The


class function is the most common type of function when creating classes.
Notice how class functions are declared inside of the class. These functions
are called class functions because they have access to the class variables
(the attributes declared in the constructor). Class functions are invoked
using dot-notation.

.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()).

Converting to Class Functions


When mutability was first introduced, you made a Player class with a few
external functions. You are now going to transform these external
functions into class functions moving forward. The Player class will be
defined just as before. This time, however, PrintPlayer will be a part of the
class.

//add class definitions below this line

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;
}
}

private: //private access modifiers


int health;
int score;
int level;
};

//add class definitions above this line


In main, instantiate a Player object. Then call the class function PrintPlayer
using dot-notation. Be sure to label the appropriate public and private
access modifiers!

//add code below this line

Player mario;
mario.PrintPlayer();

//add code above this line

challenge

Try this variation:


Call PrintPlayer like this:

Player mario;
PrintPlayer(mario);

Why did this generate an error?


C++ says that ‘PrintPlayer’ was not declared in this scope even
though the definition is within the class Player. This happens because
the code is using an external function call. Currently, PrintPlayer is a
class function within the Player class. In order to access a class
function, dot notation must be used.

More Player Methods


The next class functions to add to the Player class are those to print the
health and level attributes of the Player object. Start with the class
function ChangeHealth. This function takes amount as an int parameter.
ChangeHealth will add amount to the health attribute. If a player’s health
increases, amount is positive. If their health decreases, amount is negative.
Add ChangeHealth directly below PrintPlayer().
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;
}
}
void ChangeHealth(int amount) {
health += amount;
}

The class function NextLevel is going to be similar to ChangeHealth except


for one difference. NextLevel has no parameters. In video games, players go
up in levels; rarely do they decrease. So the level attribute will increase by
one when the class function NextLevel is called.

void ChangeHealth(int amount) {


health += amount;
}
void NextLevel() {
level++;
}

Change main to:

//add code below this line

Player mario;
mario.PrintPlayer();
mario.ChangeHealth(25);
mario.NextLevel();
mario.PrintPlayer();

//add code above this line

Then TRY IT.


challenge

Try these variations:


Change mario.ChangeHealth(25); to mario.ChangeHealth(-25);.
Add another mario.NextLevel(); below the first
mario.NextLevel();.
Create a class function to change a player’s score by a set integer
amount. Then try to call it.
One possible solution

//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

More on Class Methods and Objects


Changes to objects should happen exclusively through class functions. This
makes your code easier to organize and easier for others to understand.
Imagine you are going to create a class that keeps track of a meal. In this
case, a meal can be thought of as all of the drinks, appetizers, courses, and
desserts served. Each one of these categories will become a class variable
(attribute). Assign each attribute a vector of strings. Remember, class
variables/attribute are private.

//add class definitions below this line

class Meal {
private:
vector<string> drinks;
vector<string> appetizers;
vector<string> main_courses;
vector<string> desserts;
};

//add class definitions above this line

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;
};

//add class definitions above this line

Create a Meal object in main and then test your code with the following
added commands.

//add code below this line

Meal dinner;
dinner.AddDrink("water");
dinner.PrintDrinks();

//add code above this line

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.

//add code below this line

Meal dinner;
dinner.AddDrink("water");
dinner.PrintDrinks();
dinner.AddAppetizer("bruschetta");
dinner.PrintAppetizers();

//add code above this line

challenge

Create the following class functions:


AddMainCourse - accepts a string which represents a main course
and adds it to the meal.

PrintMainCourses - prints all of the main courses in the meal.

AddDessert - accepts a string which represents a dessert and adds it


to the meal.

PrintDesserts - prints all of the desserts in the meal.


Test your code using "roast chicken" as a main course and
"chocolate cake" as a dessert. Then use the Print class functions
you created to print out all of the items of the meal.

Meal code

#include <iostream>
#include <vector>
using namespace std;

//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;
}
}
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;
};

//add class definitions above this line

int main() {

//add code below this line

Meal dinner;
dinner.AddDrink("water");
dinner.PrintDrinks();
dinner.AddAppetizer("bruschetta");
dinner.PrintAppetizers();
dinner.AddMainCourse("roast chicken");
dinner.PrintMainCourses();
dinner.AddDessert("chocolate cake");
dinner.PrintDesserts();

//add code above this line

return 0;

}
Printing the Meal

Format the Entire Meal


Currently, you should have the following code the text editor:

#include <iostream>
#include <vector>
using namespace std;

//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;
}
}
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;
};

//add class definitions above this line

int main() {

//add code below this line

Meal dinner;
dinner.AddDrink("water");
dinner.PrintDrinks();
dinner.AddAppetizer("bruschetta");
dinner.PrintAppetizers();
dinner.AddMainCourse("roast chicken");
dinner.PrintMainCourses();
dinner.AddDessert("chocolate cake");
dinner.PrintDesserts();

//add code above this line

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:

0 Item in the Category

Drinks: None

1 Item in the Category


Drinks: water

2 Items in the Category

Drinks: water and lemonade

3 Items in the Category

Drinks: water, lemonade, and tea

Modify the Class Functions


In order to print the desired results, you’ll have to modify the class
functions. In particular, you’ll want to include multiple conditionals to
select for the printing format that you want. Note that currently, an
enhanced for loop is used to iterate the vectors. The first three sample
output is more straightforward because you can just use the size function
to check the vector’s size and then use the at function to print either 0, 1, or
2 items like below:

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.

You can compare your solution to a sample one below:

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();

Print the Entire Meal


Once you’ve completed modifying one class function. You can modify the
rest as specified. To print the entire meal, create a class function called
PrintMeal that incorporates all of the other Print functions. Once you’ve
done that, test your code with the following statements in 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();

Expected Output

Drink(s): Pepsi and Sprite


Appetizer(s): egg rolls, pot stickers, and buffalo wings
Main Course(s): smoked salmon
Dessert(s): None

Sample Code

#include <iostream>
#include <vector>
using namespace std;

//add class definitions below this line

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;
};

//add class definitions above this line

int main() {

//add code below this line

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();

//add code above this line

return 0;

}
Lab 1

Lab 1
For this lab, you will create a Student class that has the following private
class attributes:

string name - name of the student


int grade - student’s grade level
int score - test score of the student

First, let’s put together all of the 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.

//add class definitions below this line

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;
};

//add class definitions above this line

In main, try a few test cases to see if StudentStatus updates alice’s grade
level correctly.

//add code below this line

Student alice("Alice", 4);


alice.StudentStatus(60);
alice.StudentStatus(90);

//add code above this line


Since Alice received a score of 65 the first time, they were not promoted to
the next grade. However, once they got a score of 90 the second time
around, they were promoted to the next grade.
Lab 2

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.

//add class definitions below this line

class Student {
public:
Student() {
name;
grade;
}

public:
string name = "Alice";
int grade = 4;
int score = 65;
};

//add class definitions above this line

Because the code above has public class attributes, the following code in
main can change those attributes’ values directly.

//add code below this line

Student steve;
steve.name = "Steve";
cout << steve.name << endl;

//add code above this line

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;
};

//add class definitions above this line

//add code below this line

Student steve;
steve.name = "Steve";
cout << steve.name << endl;

//add code above this line

This is why understanding how class functions work is important. Class


functions serve as the intermediate step between the objects and the class
attributes. They are the ones interacting with the class attributes instead of
the user.
//add class definitions below this line

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;
};

//add class definitions above this line

//add code below this line

Student steve;
steve.ChangeName("Steve");
cout << steve.ReturnName() << endl;

//add code above this line

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.

//add class definitions below this line

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
};

//add class definitions above this line

Your task is to add the following class functions to the class:


* TotalAnimals - returns the total number of animals
* TotalMammals - returns the number of mammals (which includes big_cats
and primates)
* MostAnimals - returns the name of the animal with the most count
assuming no two animals have the same count

DO NOT CHANGE the existing code in main or you will not pass the test:

Zoo my_zoo(10, 30, 90, 120);


cout << my_zoo.TotalAnimals() << endl;
cout << my_zoo.TotalMammals() << endl;
cout << my_zoo.MostAnimals() << endl;
Zoo my_zoo2(123, 45, 67, 89);
cout << my_zoo2.TotalAnimals() << endl;
cout << my_zoo2.TotalMammals() << endl;
cout << my_zoo2.MostAnimals() << endl;
Expected Result:

250
40
birds
324
168
big cats

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy