KEMBAR78
Part 4 | PDF | Class (Computer Programming) | Constructor (Object Oriented Programming)
0% found this document useful (0 votes)
68 views170 pages

Part 4

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)
68 views170 pages

Part 4

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

Learning Objectives

Define the term encapsulation

Differentiate between public and private

Explain which parts of a class should be public or


private

Explain why encapsulation can be beneficial

Define data validation


What is Encapsulation?

What is Encapsulation?
Encapsulation is a concept in which related data and methods are grouped
together, and in which access to data is restricted. Grouping related data
and functions makes thinking about your program a bit easier. Hiding or
restricting how the user interacts with the data can keep the user from
making unwanted changes.

The two main ideas of data restriction are the public and private
keywords. These access modifiers (or keywords) can refer to classes,
functions, and attributes. public means that the constructor, function, or
attribute can be accessed both internally or externally. private means that
the constructor, attribute, or function can only be accessed internally
within the class itself.

Classes as Encapsulation
Classes in C++ are a form of encapsulation; they group together related data
and functions. In the code below, the attributes num1 and num2 are grouped
together with the functions Describe and Sum. They are all a part of
ExampleClass. The instance my_example is not a part of the class itself; it is
considered to be separate.
//add class definitions below this line

class ExampleClass {
void SetN(int n1, int n2) {
num1 = n1;
num2 = n2;
}

void Describe() {
cout << "My numbers are: " << num1 << " and " << num2 <<
endl;
}

int Sum() {
return num1 + num2;
}

int num1;
int num2;
};

//add class definitions above this line

//add code below this line

ExampleClass my_example;
my_example.SetN(5, 7);
my_example.Describe();
cout << my_example.Sum() << endl;

//add code above this line

The code above results in an error, but we’ll find out why next.

Hiding Data
You’ve learned in the Mutability chapter that class functions are typically
public and attributes are typically private. The public access modifier
enables other classes to have access to those functions. On the other hand,
the private access modifier disables access to attributes to prevent users
from imposing unwanted changes. Moving forward, we’ll abide by the
access modifier rules within the chart below.

Category Public Private


Constructor X
Functions X X
Attributes X

Why are functions both public and private?


In the pages that follow, you will see when making functions public is a
good idea, and when keeping functions private is preferable. A well
designed program will use a mix of public and private functions.
Previously, we’ve learned that helper functions can be kept private since
they are not directly accessed externally.

This is the same ExampleClass from above. It now uses the public and
private access modifiers to hide the data or to make them accessible.

//add class definitions below this line

class ExampleClass {
public:
void SetN(int n1, int n2) {
num1 = n1;
num2 = n2;
}

void Describe() {
cout << "My numbers are: " << num1 << " and " << num2 <<
endl;
}

int Sum() {
return num1 + num2;
}

private:
int num1;
int num2;
};

//add class definitions above this line

Note that when no access modifier is specified within a class, that attribute,
function, or constructor is automatically declared as private by default. To
make them public, you must specify public in the code.

Your code should run fine now because the instance or object is only
interacting with public information. Now try to print the values for the
num1 and num2 attributes.
//add code below this line

ExampleClass my_example;
my_example.SetN(5, 7);
cout << my_example.num1 << endl;
cout << my_example.num2 << endl;

//add code above this line

C++ produces an error message because an instance cannot directly access


a private attribute. This is an example of hiding data. my_example cannot
print num1 or num2 because they are private. However, my_example can
access the public methods, which can then access the private attributes.

challenge

Try this variation:


Create the functions PrintNum1 and PrintNum2 that print the num1
and num2 attributes.

Possible Solution

void PrintNum1() {
cout << num1 << endl;
}

void PrintNum2() {
cout << num2 << endl;
}

ExampleClass my_example;
my_example.SetN(5, 7);
my_example.PrintNum1();
my_example.PrintNum2();
Public Access Modifier

Public Attributes
While C++ allows you to make instance attributes public, this is not
encouraged. In fact, encapsulation asks that all attributes remain private.
C++ itself, however, allows for public attributes. The following class has
three attributes and all of them are public.

//add class definitions below this line

class Phone {
public:
Phone(string mo, int st, int me) {
model = mo;
storage = st;
megapixels = me;
}

string model;
int storage;
int megapixels;
};

//add class definitions above this line

Instantiate a Phone object and manipulate the different attributes.

//add code below this line

Phone my_phone("iPhone", 256, 12);


cout << my_phone.model << endl;
my_phone.storage = 64;
cout << my_phone.storage << endl;
cout << my_phone.megapixels + 10 << endl;

//add code above this line

When an attribute is public, a user can do whatever they want to it. This
can become problematic. In the code above, the phone’s storage was
reduced by 75%. This should not happen. Encapsulation limits what and
how information is modified. By hiding data, you can ensure that users
only manipulate the class in an approved manner.

Public Functions
Since all attributes should be private, we will use this access modifier for
the following code sample. Unlike attributes, you are encouraged to have
public functions. If all of your functions are private, it would be impossible
to interact with the object. The constructor is a special kind of function, and
this too should be public.

//add class definitions below this line

class Phone {
public:
Phone(string mo, int st, int me) {
model = mo;
storage = st;
megapixels = me;
}

private:
string model;
int storage;
int megapixels;
};

//add class definitions above this line

The real benefit of a public function is that it can access private attributes.
Public functions are the gateway to dealing with private data. Create the
public function Describe that prints a description of the Phone object.
//add class definitions below this line

class Phone {
public:
Phone(string mo, int st, int me) {
model = mo;
storage = st;
megapixels = me;
}

void Describe() {
cout << "My " << storage << " gig " << model;
cout << " has a " << megapixels << " megapixels camera."
<< endl;
}

private:
string model;
int storage;
int megapixels;
};

//add class definitions above this line

Instantiate a Phone object and call the describe method.

//add code below this line

Phone my_phone("iPhone", 256, 12);


my_phone.Describe();

//add code above this line


challenge

Try this variation


Change the access modifier from public to private.

Why does this not work?


The constructor is a special kind of function that is called when an
object is created. Once the constructor is private, it cannot be called
outside the class. That is why C++ throws an error message. The only
way a private constructor can work is if a class is declared inside
another class. The outer class can call the inner constructor even if it is
private.
Private Access Modifier

As discussed on the previous page, we will be making all attributes private.


Instance attributes, static attributes, constants — it does not matter, they
will all be private.

Private Functions
Functions too, can be private. And just like private attributes, private
functions are accessed by public functions. Here is an example class with a
private function.

//add class definitions below this line

class PrivateExample {
public:
PrivateExample(int n) {
num = n;
}

void PublicFunction() {
PrivateFunction();
}

private:
int num;

void PrivateFunction() {
cout << "The double of " << num << " is: " << num * 2 <<
endl;
cout << num << " squared is: " << num * num << endl;
}
};

//add class definitions above this line

Instantiate an object and try to call PrivateFunction.


//add code below this line

PrivateExample my_example(5);
my_example.PrivateFunction();

//add code above this line

C++ throws an error message because an instance cannot directly access a


private function. Change the function call to PublicMethod and run the code
again. This time it should work because public functions can access private
functions and/or attributes.

//add code below this line

PrivateExample my_example(5);
my_example.PublicFunction();

//add code above this line

Public and Private Methods


A well written C++ program will make use of both public and private
functions. Deciding what to make public and what to make private comes
down to how you want the user to interact with your code. Only make
public those functions you want the user to call. Keep everything else
private. The example below is a class that counts the number of vowels in a
strings.

First let’s add the private functions and attributes. Note that the IsVowel
and CountVowels functions will serve as helper functions in a later function.
This is why they do not need to be accessed directly by the user and should
be private.
//add class definitions below this line

class Words {
private:
string word;
int count;

bool IsVowel(char ch) {


ch = toupper(ch);
return (ch=='A' || ch=='E' || ch=='I' ||
ch=='O' || ch=='U');
}

int CountVowels(string str, int n) {


if (n == 1) {
return IsVowel(str[n-1]);
}
return CountVowels(str, n-1) + IsVowel(str[n-1]);
}
};

//add class definitions above this line

Next, add the public components such as the constructor Words. The
constructor instantiates an object with the word and count attributes. The
other public component is the Printer function, which calls the IsVowel
and CountVowels functions. Without this public Printer function, IsVowel
and CountVowels will be completely inaccessible.
//add class definitions below this line

class Words {
public:
Words(string str, int n) {
word = str;
count = n;
}

void Printer() {
cout << "The number of vowels in " << word;
cout << " is " << CountVowels(word, count) << endl;
}

private:
string word;
int count;

bool IsVowel(char ch) {


ch = toupper(ch);
return (ch=='A' || ch=='E' || ch=='I' ||
ch=='O' || ch=='U');
}

int CountVowels(string str, int n) {


if (n == 1) {
return IsVowel(str[n-1]);
}
return CountVowels(str, n-1) + IsVowel(str[n-1]);
}
};

//add class definitions above this line

Finally, instantiate a string to use as an argument and a Words object in


order to run the function Printer and see the program output.

//add code below this line

string s = "house";
Words vowels(s, s.length());
vowels.Printer();

//add code above this line


Learning Objectives - Getters and
Setters

Define the terms getter, setter, and data validation

Demonstrate how to access private attributes with


getters

Demonstrate how to change private attributes with


setters

Demonstrate validating data with setters


Getters

Getters
While all attributes are private, that does not mean that the user will not
need to access the values of these attributes. A public function can be used
to access a private attribute. This type of function has a special name, a
getter (also called an accessor). The whole purpose of a getter is to return
an attribute. These functions get their name because they always start with
Get followed by the attribute name.

//add class definitions below this line

class Phone {
public:
Phone(string mo, int s, int me) {
model = mo;
storage = s;
megapixels = me;
}

string GetModel() {
return model;
}

private:
string model;
int storage;
int megapixels;
};

//add class definitions above this line

The function GetModel is an example of a getter. Getters are very simple,


straightforward functions that do only one thing — return a private
attribute. Getters can be treated just as you would treat the attribute
(except for changing its value). To test a getter, you can call in main to see if
it returns what is expected.
//add code below this line

Phone my_phone("iPhone", 256, 12);


cout << my_phone.GetModel() << endl;

//add code above this line

challenge

Try this variation:


Create getters for the storage and megapixels attributes. Then call
them within main.

Possible Solution

//class definitions
public:
int GetStorage() {
return storage;
}

int GetMegapixels() {
return megapixels;
}

//main function
cout << my_phone.GetStorage() << endl;
cout << my_phone.GetMegapixels() << endl;

Benefits of Getters
Using a getter is the same thing as accessing a public attribute. Why not
make the attribute public? That would mean writing less code. Is that not a
good thing? A public attribute makes no distinction between accessing its
value and changing its value. If you can access it, you can change it (or vice
versa). Using a getter with a private attribute makes this distinction clear;
you can access the value, but you cannot change it.
//add code below this line

Phone my_phone("iPhone", 256, 12);


cout << my_phone.GetModel() << endl;
my_phone.model = "Pixel 5";

//add code above this line

The code above generates an error because an instance cannot alter a


private attribute. Using a getter allows limited access to an attribute, which
is preferable to the full access a public access modifier allows.
Setters

Setters
Setters are the compliment to getters in that they allow you to set the value
of a private attribute. Setter functions are also called mutators. Use the
Phone class from the previous page.

//add class definitions below this line

class Phone {
public:
Phone(string mo, int s, int me) {
model = mo;
storage = s;
megapixels = me;
}

string GetModel() {
return model;
}

int GetStorage() {
return storage;
}

int GetMegapixels() {
return megapixels;
}

private:
string model;
int storage;
int megapixels;
};

//add class definitions above this line

Add the SetModel method to the Phone class. As this is a setter method, start
the name with Set followed by the name of the attribute. Setters do not
return anything. Finally, setters have a parameter — the new value for the
attribute.
public:
// Setter
void SetModel(string new_model) {
model = new_model;
}

Now that you are implementing both getters and setters, you should now
be able to access and modify private attributes.

//add code below this line

Phone my_phone("iPhone", 256, 12);


cout << my_phone.GetModel() << endl;
my_phone.SetModel("XR");
cout << my_phone.GetModel() << endl;

//add code above this line


challenge

Try this variation:


Create setters for the storage and megapixels attributes. Then call them
within main.

Possible Solution

//class definitions
public:
void SetStorage(int new_storage) {
storage = new_storage;
}

void SetMegapixels(int new_megapixels) {


megapixels = new_megapixels;
}

//main function
my_phone.SetStorage(128);
cout << my_phone.GetStorage() << endl;
my_phone.SetMegapixels(6);
cout << my_phone.GetMegapixels() << endl;

Comparing Getters and Setters


Getters and setters have a lot in common. Their names are similar, they
have the same number of lines of code, etc. However, getters and setters
also differ in a few important ways. The table below highlights these
similarities and differences.

Category Getters Setters


Has public keyword X X
Has private keyword - -
Has return statement X -
Has void type - X
Performs only 1 task X X
Has parameter - X
Data Validation

C++ already has a type system which will flag errors when a boolean value
is passed to a function that takes an integer. However, just because a
function takes an integer does not mean that all integers are valid for that
function. Data validation is the process of asking if this data is appropriate
for its intended use. Take a look at the Person class.

//add class definitions below this line

class Person {
public :
Person(string n, int a) {
name = n;
age = a;
}

string GetName() {
return name;
}

void SetName(string new_name) {


name = new_name;
}

int GetAge() {
return age;
}

void SetAge(int new_age) {


age = new_age;
}

private:
string name;
int age;
};

//add class definitions above this line

The SetAge function will assign any value to the attribute age as long as the
value is an integer. There are some integers which make no sense when
thought of as an age attribute. The code sample below sets the age of
my_person to -100. -100 is a valid integer, but it is not a valid age. This is
why data validation is important. C++’s compiler is not sufficient to catch all
errors.

//add code below this line

Person my_person("Calvin", 6);


cout << my_person.GetName() << " is " << my_person.GetAge() <<
" years old." << endl;
my_person.SetAge(-100);
cout << my_person.GetName() << " is " << my_person.GetAge() <<
" years old." << endl;

//add code above this line

Another benefit of using setters is that data validation can take place before
the new value is assigned to the attribute. Modify SetAge so that it will only
update the age attribute if the new value is greater than or equal to 0. This
way you can ensure that the Person object always has a valid age.

void SetAge(int new_age) {


if (new_age >= 0) {
age = new_age;
}
}
challenge

Try these variations:


Change the data validation for the SetAge function so that values
over 200 or less than 0 are not valid.

Possible Solution

Here is one possible solution.

void SetAge(int new_age) {


if (new_age >= 0 && new_age <= 200) {
age = new_age;
}
}

Add data validation to the SetName function so that all strings with
one or more characters are valid.

Possible Solution

Here is one possible solution.

void SetName(string new_name) {


if (new_name != "") {
name = new_name;
}
}
Encapsulation Lab 1

This lab will focus on building on the idea of a “black box.” The term “black
box” is used to describe a system in which the internal workings are
hidden from the user. In fact, the user is not expected to know how the
system works; they only need to know how to use it. Encapsulation allows
you to create classes that are black boxes. Previously, we created a class
Words that took in a string and printed the number of vowels in that string.

.guides/img/Encapsulation/BlackBox3

In this lab, we are going to create a function that gives the user the ability
to put in a vector of strings and our program will print out the number of
vowels in each string within that vector.

//add code below this line

vector<string> list = {"house", "cake", "pancake"};


Words vowels(list);
vowels.CoutStrings();

//add code above this line

If you run the code above successfully in main, you should expect to see
3,2,3 as the output.

.guides/img/Encapsulation/BlackBox4

In this scenario, we are going to modify our previous Words class so that it
can include a vector. All of the following code will go into the class
definitions field.
class Words {
private:
vector<string> list_of_words;
};

We don’t need to reinvent the wheel; we previously worked on a few


functions that told us how many vowels exist in a specific string. We are
going to again re-use those functions. These are functions that the user does
not need to interact with. Thus, they can be private, essentially making
them act as part of the “black box.” Now, add the following private
functions into the Words class.

bool IsVowel(char ch) {


ch = toupper(ch);
return (ch=='A' || ch=='E' || ch=='I' ||
ch=='O' || ch=='U');
}

int CountVowels(string str, int n) {


if (n == 1) {
return IsVowel(str[n-1]);
}
return CountVowels(str, n-1) + IsVowel(str[n-1]);
}

Now that all of the data that we don’t want the user to manipulate are
properly encapsulated as private, we can start working on our public
members such as the constructor Words and the function CoutStrings. In
the example above, we can see our constructor takes in one attribute.

public:
Words(vector<string>& n){
list_of_words = n;
}

Another piece of information that we have from the main function is that
the function CoutStrings is used. This function has no arguments, but is
accessible to the user. This function also has several other tasks:

1. It iterates through a given vector


2. Counts the number of vowels in each given string
3. Creates a vector storing the vowel sizes
4. Prints out the output in a defined fashion
void CoutStrings() {
vector<int> vowel_sizes;
int size;
for (auto a : list_of_words) {
size = CountVowels(a, a.length());
vowel_sizes.push_back(size);
}
for (int i = 0; i < vowel_sizes.size(); i++) {
if (i == vowel_sizes.size()-1) {
cout << vowel_sizes.at(i) << endl;
}
else {
cout << vowel_sizes.at(i) << ',';
}
}
}

Full Code

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

//add class definitions below this line

class Words {
public:
Words(vector<string>& n){
list_of_words = n;
}

void CoutStrings() {
vector<int> vowel_sizes;
int size;
for (auto a : list_of_words) {
size = CountVowels(a, a.length());
vowel_sizes.push_back(size);
}
for (int i = 0; i < vowel_sizes.size(); i++) {
if (i == vowel_sizes.size()-1) {
cout << vowel_sizes.at(i) << endl;
}
else {
cout << vowel_sizes.at(i) << ',';
}
}
}

private:
vector<string> list_of_words;

bool IsVowel(char ch) {


ch = toupper(ch);
return (ch=='A' || ch=='E' || ch=='I' ||
ch=='O' || ch=='U');
}

int CountVowels(string str, int n) {


if (n == 1) {
return IsVowel(str[n-1]);
}
return CountVowels(str, n-1) + IsVowel(str[n-1]);
}
};

//add class definitions above this line

int main() {

//add code below this line

vector<string> list = {"house", "cake", "pancake"};


Words vowels(list);
vowels.CoutStrings();

//add code above this line

return 0;

Challenge
Can you modify the code so that it prints the number of non-vowels
instead of vowels? For example, vector<string> list_of_words =
{"house", "cake", "pancake"}; will have the following output: 2,2,4.

Possible Solution

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

//add class definitions below this line

class Words {
public:
Words(vector<string>& n){
list_of_words = n;
}

void CoutStrings() {
vector<int> counts;
int size;
for (auto a : list_of_words) {
size = CountNonVowels(a, a.length());
counts.push_back(size);
}
for (int i = 0; i < counts.size(); i++) {
if (i == counts.size()-1) {
cout << counts.at(i) << endl;
}
else {
cout << counts.at(i) << ',';
}
}
}

private:
vector<string> list_of_words;

bool IsNonVowel(char ch) {


ch = toupper(ch);
return !(ch=='A' || ch=='E' || ch=='I' ||
ch=='O' || ch=='U');
}

int CountNonVowels(string str, int n) {


if (n == 1) {
return IsNonVowel(str[n-1]);
}
return CountNonVowels(str, n-1) + IsNonVowel(str[n-
1]);
}
};

//add class definitions above this line


int main() {

//add code below this line

vector<string> list = {"house", "cake", "pancake"};


Words nonvowels(list);
nonvowels.CoutStrings();

//add code above this line

return 0;

}
Encapsulation Lab 2

In this next lab, we’ll continuing building our Words class to include a getter
and setter. These should be public to enable the main function to call them.

Full Code

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

//add class definitions below this line

class Words {
public:
Words(vector<string>& n){
list_of_words = n;
}

void CoutStrings() {
vector<int> vowel_sizes;
int size;
for (auto a : list_of_words) {
size = CountVowels(a, a.length());
vowel_sizes.push_back(size);
}
for (int i = 0; i < vowel_sizes.size(); i++) {
if (i == vowel_sizes.size()-1) {
cout << vowel_sizes.at(i) << endl;
}
else {
cout << vowel_sizes.at(i) << ',';
}
}
}

private:
vector<string> list_of_words;

bool IsVowel(char ch) {


ch = toupper(ch);
return (ch=='A' || ch=='E' || ch=='I' ||
ch=='O' || ch=='U');
}

int CountVowels(string str, int n) {


if (n == 1) {
return IsVowel(str[n-1]);
}
return CountVowels(str, n-1) + IsVowel(str[n-1]);
}
};

//add class definitions above this line

int main() {

//add code below this line

vector<string> list = {"house", "cake", "pancake"};


Words vowels(list);
vowels.CoutStrings();

//add code above this line

return 0;

First, we should determine what attribute to get and set. We currently have
only one attribute, list_of_words, which is used to store the list of strings.
However, what if we want to get a particular string? Or perhaps, set a
particular string at an index?

To be able to return a string from the vector, we’ll need to specify an index
or position. This means our getter function will need to take in an integer
parameter. However, there is a limitation to the index we can specify. We
cannot specify an index position that does not exist. Therefore, we’ll need
to validate the given parameter before returning anything.

string GetString(int i) {
if (i >= list_of_words.size()) {
return "No string exists at this index.";
}
return list_of_words.at(i);
}
GetString checks to see if the index exists before it returns a string within
the vector. If the index does not exist, No string exists at this index.
will be returned to the user. Otherwise, the function will return the string
at the specified index.

Next, let’s create our setter. This setter function will first check to see if the
index exists, then it will set a particular string specified by the user into
that index. Therefore, we’ll need two parameters for our setter.

void SetString(string s, int i) {


if (i >= list_of_words.size()) {
cout << "No string exists at this index." << endl;
}
list_of_words.at(i) = s;
}

Let’s test the code with the following commands in main.

//add code below this line

vector<string> list = {"house", "cake", "pancake"};


Words words(list);
cout << words.GetString(0) << endl;
words.SetString("mouse", 0);
cout << words.GetString(0) << endl;

//add code above this line

After the vector is accepted as a parameter, the GetString function is able


to return the string specified at index 0. This is why house is printed. Then,
SetString takes in mouse as a parameter and replaces the word at index 0
with it. GetString is then called again. Since mouse has replaced house, mouse
is printed during the second call.

Updated Code

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

//add class definitions below this line

class Words {
public:
Words(vector<string> n){
list_of_words = n;
}

string GetString(int i) {
if (i >= list_of_words.size()) {
return "No string exists at this index.";
}
return list_of_words.at(i);
}

void SetString(string s, int i) {


if (i >= list_of_words.size()) {
cout << "No string exists at this index." << endl;
}
list_of_words.at(i) = s;
}

void CoutStrings() {
vector<int> vowel_sizes;
int size;
for (auto a : list_of_words) {
size = CountVowels(a, a.length());
vowel_sizes.push_back(size);
}
for (int i = 0; i < vowel_sizes.size(); i++) {
if (i == vowel_sizes.size()-1) {
cout << vowel_sizes.at(i) << endl;
}
else {
cout << vowel_sizes.at(i) << ',';
}
}
}

private:
vector<string> list_of_words;

bool IsVowel(char ch) {


ch = toupper(ch);
return (ch=='A' || ch=='E' || ch=='I' ||
ch=='O' || ch=='U');
}

int CountVowels(string str, int n) {


if (n == 1) {
return IsVowel(str[n-1]);
}
return CountVowels(str, n-1) + IsVowel(str[n-1]);
}
};

//add class definitions above this line

int main() {

//add code below this line

vector<string> list = {"house", "cake", "pancake"};


Words words(list);
cout << words.GetString(0) << endl;
words.SetString("mouse", 0);
cout << words.GetString(0) << endl;

//add code above this line

return 0;

}
Lab Challenge

Lab Challenge
Problem
Write a class named Person that has attributes string name, int age, and
string occupation. These attributes should be private. Create getters and
setters for each attribute following C++ conventions.

Given Code

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

//add class definitions below this line

//add class definitions above this line

int main() {

//DO NOT EDIT CODE BELOW THIS LINE

Person p("Citra Curie", 16, "student");


cout << p.GetName() << endl;
p.SetName("Rowan Faraday");
cout << p.GetAge() << endl;
p.SetAge(18);
cout << p.GetOccupation() << endl;
p.SetOccupation("plumber");
cout << p.GetName() << endl;
cout << p.GetAge() << endl;
cout << p.GetOccupation() << endl;

//DO NOT EDIT CODE ABOVE THIS LINE

return 0;

}
Requirements
* Declare the instance Person("Citra Curie", 16, "student")
* The function GetName() returns Citra Curie
* The function SetName("Rowan Faraday") changes the name attribute to
“Rowan Faraday”
* The function GetAge() returns 16
* The function SetAge(18) changes the age attribute to 18
* The function GetOccupation() returns student
* The function SetOccupation("plumber") changes the occupation attribute
to “plumber”
* DO NOT EDIT the specified code in the main function or you may not
receive credit for your work!

Expected Output

Citra Curie
16
student
Rowan Faraday
18
plumber

Testing Your Code


Use the button below to test your code before submitting it for evaluation.
Learning Objectives: Base &
Derived Classes

Define the terms inheritance, base class, and derived


class

Explain the relationship between the base class and the


derived class

Create a derived class from a given base class

Understand how access modifiers work with inheritance


types

Compare base and derived classes

Define Liskov’s Substitution Principle


What is Inheritance?

Defining Inheritance
Imagine you want to create two C++ classes, Person and Superhero. These
respective classes might look something like this:

.guides/img/inheritance/CppNoInheritance

There are some similarities between the Person class and the Superhero
class. If the Person class already exists, it would be helpful to “borrow”
from the Person class so you only have to create the new attributes and
functions for the Superhero class. This situation describes inheritance —
one class copies the attributes and functions from another class.

Inheritance Syntax
In the IDE on the left, the Person class is already defined. To create the
Superhero class that inherits from the Person class, add the following code
at the end of the class definitions. Notice how the Superhero class definition
contains a colon : followed by public and then Person. This is how you
indicate to C++ that the Superhero class inherits from the Person class. You
can also say that Person is the base class and Superhero is the derived
class. A base class in C++ is also referred to as a superclass or parent class
while a derived class is also referred to as a subclass or child class. All of
these terms can be used interchangeably.
//add class definitions below this line

class Superhero : public Person {

};

//add class definitions above this line

Now declare an instance of the Superhero class and print the value of the
name and age attributes using their getter functions.

//add code below this line

Superhero s;
cout << s.GetName() << endl;
cout << s.GetAge() << endl;

//add code above this line

What does the output of the program above mean?


In C++, reducing memory usage is important and initializing values to
variables requires memory. Uninitialized variables do not get assigned
specified values automatically. Thus, when printing the value of
uninitialized variables, you might get random and unexpected output. The
output is considered to be junk data that are left over at the variables’
memory location.
challenge

Try these variations:


Call SetName on s with "Peter Parker" as the argument.
Call SetOccupation on s with "Student" as the argument.
Print and call GetOccupation on s.
Call SayHello on s.

Solution

//add code below this line

Superhero s;
s.SetName("Peter Parker");
s.SetOccupation("Student");
cout << s.GetOccupation() << endl;
s.SayHello();

//add code above this line

Accessibility of Inheritance
C++ places some rules about how inheritance works. Depending on the
access modifier of the specified superclass, the subclass may or may not
inherit certain class functions or attributes. Here is a list showcasing each
access modifier and its effect on the subclass.

“public inheritance makes public members of the base class public in


the derived class, and the protected members of the base class remain
protected in the derived class.”
“protected inheritance makes the public and protected members of the
base class protected in the derived class.”
“private inheritance makes the public and protected members of the
base class private in the derived class.”

Source: https://www.programiz.com/cpp-programming/public-protected-
private-inheritance

The list above can be represented using the chart below:


.guides/img/inheritance/InheritanceChart

Source: https://www.bogotobogo.com/cplusplus/private_inheritance.php

Notice how in each case, the derived class never inherits any private
members from the base class. Public inheritance causes the derived class to
inherit the members as is. Protected inheritance causes the derived class to
inherit all public and protected members as protected only. And private
inheritance causes all inherited members to be private only.

You can see how restrictive protected and private inheritance is, which is
why they are rarely used. For this module, we will be using mainly public
inheritance when creating derived classes.
Effects of Inheritance Types 1

Access Modifiers Review


Before we can look at the effects of inheritance types, we will need to
review some access modifier vocabulary:

private - private members of a class can only be accessed by other


members within the same class.
protected - protected members of a class can be accessed by other
members within the same class or by a derived class.
public - public members of a class can be accessed by other members
within the same class, by a derived class, or by an external or outside
class.

The table below showcases these access modifiers’ effects.

.guides/img/inheritance/AccessModifierTable

Source: https://www.tutorialspoint.com/cplusplus/cpp_inheritance.htm

Accessing Class Members


In the text editor, the Base class has already been defined. Within this base
class, you’ll see that there is a public function, a protected function, and a
private function. Add the following derived class to your code:
//add class definitions below this line

class Derived : public Base {


public:
void ReturnPublic(string s) {
Public(s_derived); //public function inherited from Base
}

private:
string s_derived;
};

//add class definitions above this line

The derived class Derived has one public function and one private attribute
and it publicly inherits all public and protected members of the base class
Base. This means that it can call and use any functions or attributes of Base
with the exception of anything that is private.

Next, add the following code into the main function:

//add code below this line

string s_main;
Derived dc;
dc.ReturnPublic(s_main);

//add code above this line

In main, a string s_main and a Derived object dc are created. Then


ReturnPublic() is called on dc with s_main as the parameter. ReturnPublic
is a public member function of the Derived class which takes a string as a
parameter and calls the Public member function of the Base class on
s_derived. The entire process sounds very complicated but can be
explained visually in the flowchart below.
.guides/img/inheritance/FlowChart1

The reason why main is able to call ReturnPublic is due to the fact that
ReturnPublic is a public member function within Derived.

challenge

Try this variation:


Revise Derived to look like:

class Derived : public Base {


protected:
void ReturnPublic(string s) {
Public(s_derived);
}

private:
string s_derived;
};

When the ReturnPublic member function of Derived is protected, main is


no longer able to access it. Remember, external classes can only access
public members of other classes, unless it is a derived class. Derived
classes can access protected members in addition to public ones.
Next, let’s change Derived and main to look like this:

//add class definitions below this line

class Derived : public Base {


public:
void ReturnPublic(string s) {
Public(s_derived); //public function inherited from Base
}

void ReturnProtected(string s) {
Protected(s_derived); //protected function inherited from
Base
}

private:
string s_derived;
};

//add class definitions above this line

//add code below this line

string s_main;
Derived dc;
dc.ReturnProtected(s_main);
dc.Public(s_main);

//add code above this line

Notice how main can call Public and also ReturnProtected from Derived
because those functions are public. Additionally, Derived can call the
protected function Protected in ReturnProtected from Base because
Derived is a derived class of Base.
.guides/img/inheritance/FlowChart2
Effects of Inheritance Types 2

How Inheritance Works


Now that you’re more comfortable with access modifiers, let’s take a look at
how inheritance types affect derived classes. Make sure your Derived and
main code look like what’s below. Note that Derived now inherits from Base
through protected inheritance instead of public.

//add class definitions below this line

class Derived : protected Base {


public:
void ReturnPublic(string s) {
Public(s_derived);
}

void ReturnProtected(string s) {
Protected(s_derived);
}

private:
string s_derived;
};

//add class definitions above this line

//add code below this line

string s_main;
Derived dc;
dc.ReturnProtected(s_main);
dc.Public(s_main);

//add code above this line

You’ll notice that an error is produced saying that ‘Base’ is not an


accessible base of ‘Derived’. This occurs because when a derived class
inherits from a base class through protected inheritance, all public and
protected members of the base class become protected in the derived class.
This means that the Public function within Derived was inherited as a
protected function and is therefore no longer accessible within main.

On the other hand, ReturnProtected is still a public function within


Derived, which allows main to access it even though it calls the protected
function Protected from Base. Comment out the command
dc.Public(s_main); and run the code again to see the result.

Though not explicitly shown, Derived can be represented like this:

class Derived : protected Base {


public:
void ReturnPublic(string s) {
Public(s_derived);
}

void ReturnProtected(string s) {
Protected(s_derived);
}

/*protected:
void Public(string s) { (inherited as protected from Base)
s = "public";
cout << s << endl;
}

void Protected(string s) { (inherited as protected from


Base)
s = "protected";
cout << s << endl;
}*/

private:
string s_derived;
};

If you ever get confused with inheritance types, you can always
imaginatively rewrite all of the functions that were inherited and label
them appropriately to help you visualize the derived class better. Here are
some more examples:
class Base {
public:
int x;

protected:
int y;

private:
int z;
};

class Derived : public Base {


//public:
//int x;

//protected:
//int y;
};

class Base {
public:
int x;

protected:
int y;

private:
int z;
};

class Derived : protected Base {


//protected:
//int x;
//int y;
};
class Base {
public:
int x;

protected:
int y;

private:
int z;
};

class Derived : private Base {


//private:
//int x;
//int y;
};

Note that private members are never inherited. Also, the examples are
provided to help you visualize what the derived classes look like after
inheritance. If you were to actually add the member attributes into Derived
without comments, those attributes will count as being part of the Derived
class instead of being inherited from the Base class.
Inheriting the Constructor

How are Constructors Inherited?


Unlike functions and attributes, the constructor that is inherited by the
derived class needs to get linked or associated with that of the base class. To
connect the derived constructor to the base constructor, follow this syntax:

Derived class name


Parentheses ()
Parameters with specified data types within parentheses
Colon :
Base class name
Parentheses ()
Parameters of derived class as arguments in parentheses
Any additional commands in curly braces {} or leave them empty

Sample Code:
Constructor2(Param p1, Param p2) : Constructor1(p1, p2) {}

Add the following code to the class definitions field:

//add class definitions below this line

class Superhero : public Person {


public:
Superhero(string n2, int a2) : Person(n2, a2) {}
};

//add class definitions above this line

And the following to main:

//add code below this line

Superhero s("Spider-Man", 16);


s.ReturnPerson();

//add code above this line


By associating the derived constructor with the base constructor, C++ is
able to pass the parameters specified in the derived constructor as
arguments of the base constructor. In the code above, the arguments
Spider-Man and 16 are passed to the Superhero constructor and then
transferred over to the Person constructor where they get assigned to name
and age respectively. Then, the ReturnPerson function is used to print name
which is now Spider-Man and age which is now 16.
Comparing Base & Derived Classes

Determining a Derived Class’s Base Class


How do you determine if a derived class actually belongs to a base class?
One common way to determine this is to use the is_base_of<Base,
Derived>::value function. Just substitute Base with the name of the base
class and Derived with the name of the derived class.

//add code below this line

cout << boolalpha;


cout << "B is derived from A: " << is_base_of<A, B>::value <<
endl;
cout << "C is derived from B: " << is_base_of<B, C>::value <<
endl;
cout << "A is derived from C: " << is_base_of<C, A>::value <<
endl;

//add code above this line

Here is an example:

//add code below this line

cout << boolalpha;


cout << "Superhero is derived from Person: " <<
is_base_of<Person, Superhero>::value << endl;
cout << "Animal is derived from Superhero: " <<
is_base_of<Superhero, Animal>::value << endl;
cout << "Person is derived from Animal: " <<
is_base_of<Animal, Person>::value << endl;

//add code above this line


challenge

Try this variation:


Add the following to main:

cout << "Person is derived from Superhero: ";


cout << is_base_of<Superhero, Person>::value << endl;

You’ll notice that the function returns true if the derived class inherits from
the base class and false when that is not the case.

Determining an Object’s Base Class


Unfortunately, C++ has no built-in function to determine if an object is from
a class that inherits from another class. Instead, use
typeid(<object_name>).name() to try and extract the object’s type, and then
use the is_base_of<Base, Derived>::value to see if that object’s class is
derived from another specified class. Replace <object_name> with the name
of the object.

Remove all existing code in main and add the following:

//add code below this line

Superhero s;
cout << "s is of type: " << typeid(s).name() << endl;

//add code above this line

Your output may look something like s is of type: 9Superhero. The 9 is


just a number that is produced by the compiler, which can be ignored.
Once you determine the object’s class, you can then compare that class to
another class to see if it is a derived class.
//add code below this line

Superhero s;
cout << "s is of type: " << typeid(s).name() << endl;
cout << boolalpha;
cout << "Superhero is derived from Person: ";
cout << is_base_of<Person, Superhero>::value << endl;

//add code above this line

The functions provided above can help you determine an object’s class and
compare it with another known class. There is unfortunately no function to
determine an object’s base class directly.
Substitution Principle

Substitution Principle
When one class inherits from another, C++ considers them to be related.
They may contain different data types, but C++ allows a derived class to be
used in place of the base class. This is called Liskov’s Substitution
Principle. In the text editor you’ll notice that Superhero inherits from
Person, but Animal does not. All classes have the Greeting function which
prints a statement specific to the class.

//add class definitions below this line

class Person {
public:
void Greeting() {
cout << "I'm a Person" << endl;
}
};

class Superhero : public Person {


public:
void Greeting() {
cout << "I'm a Superhero" << endl;
}
};

class Animal {
public:
void Greeting() {
cout << "I'm an Animal" << endl;
}
};

//add class definitions above this line

According to the Substitution Principle, an object of Superhero can be used


in a situation that expects an object of Person. Add the Substitution
function below which explicitly requires a parameter of a Person object.
//add function definitions below this line

void Substitution(Person p) {
p.Greeting();
}

//add function definitions above this line

Instantiate an object of Superhero and pass it to the Substitution function.


Even though the object s has the wrong data type, the code should still
work due to the Substitution Principle. Because Superhero is derived from
Person, object s can be used in place of an object of type Person. Run the
code to verify the output.

//add code below this line

Superhero s;
Substitution(s);

//add code above this line

challenge

Try this variation:


Revise main to:

Animal a;
Substitution(a);

Why did this produce an error?


The Animal class is not derived from Person, therefore the Substitution
Principle no longer applies.

The Substitution Principle is a One-Way


Relationship
Let’s revise the Substitution function and main to look like below:
//add function definitions below this line

void Substitution(Superhero s) {
s.Greeting();
}

//add function definitions above this line

//add code below this line

Person p;
Substitution(p);

//add code above this line

The code above produces an error stating that the object p cannot be
converted to a Superhero object. Kind of like how a square is considered to
be a rectangle, but a rectangle is not considered to be a square, inheritance
works the same way. A Superhero object is considered to be a Person object,
but a Person object is not considered to be Superhero object.

Inheritance Can Be Extended


Inheritance can be extended, which means that a derived class can inherit
attributes of another derived class. Revise the class definitions to look like
below:
//add class definitions below this line

class Person {
public:
void Greeting() {
cout << "I'm a Person" << endl;
}
};

class Hero : public Person {


public:
void Greeting() {
cout << "I'm a Hero" << endl;
}
};

class Superhero: public Hero {


public:
void Greeting() {
cout << "I'm a Superhero" << endl;
}
};

//add class definitions above this line

You’ll notice that we have an intermediate class called Hero which is a


derived class of Person. The Superhero class is then derived from the Hero
class. When there are multiple levels of inheritance, the immediate upper
class is considered to be a direct base class and all higher classes are
considered to be indirect base classes. This means that Hero is a direct base
class of Superhero while Person is an indirect base class of Superhero.
Superhero is still considered to be a derived class of both Hero and Person.

See what happens when you you revise the Substitution function and main
to look like below:

//add function definitions below this line

void Substitution(Person p) {
p.Greeting();
}

//add function definitions above this line


//add code below this line

Superhero s;
Substitution(s);
Hero h;
Substitution(h);

//add code above this line

Whether the object is of class Superhero or Hero, the Substitution function


still works because both are derived classes of Person.
Learning Objectives: Extending &
Overriding

Define the terms extending and overriding

Extend the derived class with a new function

Override functions from the base class with new


functionality
Extending a Class

Extending the Derived Class


The idea of inheritance is to borrow from a base class and then add on
functionality. Up until now, we have talked about borrowing from a base
class but have not gone into detail about adding additional functionality to
the derived class. The process of adding functionality to a derived class is
known as either extending or overriding. Extending a class means that new
attributes and functions are given to the derived class. Let’s continue
working with our Person and Superhero classes.

.guides/img/inheritance/CppNoInheritance

The code below will first associate the derived class constructor with the
base class constructor. However, we want to add an additional attribute as
a parameter to the derived class constructor. Doing so will extend the
derived class because objects created will have 4 parameter attributes
instead of 3. Additional getter and setter functions are also added to extend
the derived class even further.
//add class definitions below this line

class Superhero : public Person {


public:
Superhero(string n, int a, string o, string s) : Person(n,
a, o) {
secret_identity = s;
}

string GetSecretIdentity() {
return secret_identity;
}

void SetSecretIdentity(string new_secret_identity) {


secret_identity = new_secret_identity;
}

private:
string secret_identity;
};

//add class definitions above this line

In main, instantiate a Superhero object and print out each of the attributes.
You should see the three attributes from the Person class as well as the new
attribute secret_identity.

//add code below this line

Superhero hero("Spider-Man", 16, "student", "Peter Parker");


cout << hero.GetName() << endl;
cout << hero.GetAge() << endl;
cout << hero.GetOccupation() << endl;
cout << hero.GetSecretIdentity() << endl;

//add code above this line

Inheritance Is a One-Way Street


Inheritance shares attributes and functions from the base class to the
derived class. When a derived class is extended, it cannot share the new
additions with its base class. For example, in the code above, the Superhero
class has access to the attributes name, age, and occupation, but Person does
not have access to secret_identity.
challenge

Try this variation:


Rewrite the Superhero class so that it extends the Person class by
adding the string attribute nemesis. The constructor will also
include nemesis as the last parameter with Doc Octopus as its
argument. Remember to include the relevant getter and setter
functions too!
Solution

//add class definitions below this line

class Superhero : public Person {


public:
Superhero(string n, int a, string o, string s, string
ne) : Person(n, a, o) {
secret_identity = s;
nemesis = ne;
}

string GetSecretIdentity() {
return secret_identity;
}

void SetSecretIdentity(string new_secret_identity) {


secret_identity = new_secret_identity;
}

string GetNemesis() {
return nemesis;
}

void SetNemesis(string new_nemesis) {


nemesis = new_nemesis;
}

private:
string secret_identity;
string nemesis;
};

//add class definitions above this line

int main() {
//add code below this line

Superhero hero("Spider-Man", 16, "student", "Peter


Parker", "Doc Octopus");
cout << hero.GetName() << endl;
cout << hero.GetAge() << endl;
cout << hero.GetOccupation() << endl;
cout << hero.GetSecretIdentity() << endl;
cout << hero.GetNemesis() << endl;

//add code above this line

return 0;

Extending a Class by Adding Unique Functions


Another way to extend a class is to create new functions that are unique to
the derived class (besides getter and setter functions). Currently, the
function SayHello will print the superhero’s name, but it will not print their
secret identity. Create the function RevealSecretIdentity to print a greeting
that reveals SecretIdentity.
//add class definitions below this line

class Superhero : public Person {


public:
Superhero(string n, int a, string o, string s, string ne) :
Person(n, a, o) {
secret_identity = s;
nemesis = ne;
}

string GetSecretIdentity() {
return secret_identity;
}

void SetSecretIdentity(string new_secret_identity) {


secret_identity = new_secret_identity;
}

string GetNemesis() {
return nemesis;
}

void SetNemesis(string new_nemesis) {


nemesis = new_nemesis;
}

void RevealSecretIdentity() {
cout << "My real name is " << secret_identity << '.' <<
endl;
}

private:
string secret_identity;
string nemesis;
};

//add class definitions above this line

Now test out the newly added function.

//add code below this line

Superhero hero("Spider-Man", 16, "student", "Peter Parker",


"Doc Octopus");
hero.RevealSecretIdentity();

//add code above this line


challenge

Try this variation:


Create the function SayNemesis that prints the string
My nemesis is Doc Octopus., then call it on hero in main.
Solution

//add class definitions below this line

class Superhero : public Person {


public:
Superhero(string n, int a, string o, string s, string
ne) : Person(n, a, o) {
secret_identity = s;
nemesis = ne;
}

string GetSecretIdentity() {
return secret_identity;
}

void SetSecretIdentity(string new_secret_identity) {


secret_identity = new_secret_identity;
}

string GetNemesis() {
return nemesis;
}

void SetNemesis(string new_nemesis) {


nemesis = new_nemesis;
}

void RevealSecretIdentity() {
cout << "My real name is " << secret_identity << '.'
<< endl;
}

void SayNemesis() {
cout << "My nemesis is " << nemesis << '.' << endl;
}

private:
string secret_identity;
string nemesis;
};

//add class definitions above this line

int main() {

//add code below this line

Superhero hero("Spider-Man", 16, "student", "Peter


Parker", "Doc Octopus");
hero.SayNemesis();

//add code above this line

return 0;

}
Function Overriding

Overriding a Function
Extending a class means adding new attributes or functions to the derived
class. Another way to add new functionality to a derived class is through
function overriding. Overriding a function means to inherit a function
from the base class, keep its name, but change the contents of the function.

Extend the Superhero class by overriding the SayHello function. Remember,


the name attribute is part of the base class and it is private, so you need to
use the GetName function to access this attribute.

//add class definitions below this line

void SayHello() {
cout << "My name is " << GetName() << ", and criminals
fear me." << endl;
}

//add class definitions above this line

Instantiate a Superhero object and call the SayHello function on it.

//add code below this line

Superhero hero("Storm", 30, "Queen of Wakanda", "Ororo


Munroe", "Shadow King");
hero.SayHello();

//add code above this line

Differentiating Overriding and Extending


The difference between extending and overriding can be slight. Both
approaches are used to make a derived class unique from the base class.
However, overriding deals with changing a pre-existing function from the
base class, while extending deals with adding new functions and
attributes.
challenge

Try this variation:


Add and override the SayAge function in the Superhero class so that
it prints the string, Age is just a number., then call it on hero in
the main function.
Solution

void SayAge() {
cout << "Age is just a number." << endl;
}

Superhero hero("Storm", 30, "Queen of Wakanda", "Ororo


Munroe", "Shadow King");
hero.SayAge();

What Happens to the Overridden Function?


If you can override a function from the base class, what happens to its
original function? C++ defaults to the instance or object type. So
hero.SayHello() will always use the function from the derived Superhero
class. But that does not mean you cannot call SayHello from the base Person
class. To call the original base class function, you can use
hero.Person::SayAge() where hero represents the derived class object,
Person represents the base class, and SayAge represents the base class
function. The :: is called the scope resolution operator and it is used to
direct C++ to look for the function SayAge inside the Person class. Make sure
you have the following class definitions in your code.
//add class definitions below this line

void SayHello() {
cout << "My name is " << GetName() << ", and criminals
fear me." << endl;
}

void SayAge() {
cout << "Age is just a number." << endl;
}

//add class definitions above this line

Then run the following commands in main to see the result.

//add code below this line

Superhero hero("Storm", 30, "Queen of Wakanda", "Ororo


Munroe", "Shadow King");
hero.SayHello();
hero.Person::SayHello();
hero.SayAge();
hero.Person::SayAge();

//add code above this line

Note how in the code above, using the scope resolution operator causes the
base class function to be called while not using it causes the derived class
function to be called.
challenge

Try this variation:


Modify the following code in the Person class:

void SayHello() {
cout << "Hello, my name is " << name << '.' << endl;
}

void SayAge() {
cout << "I am " << age << " years old." << endl;
}

to

virtual void SayHello() final {


cout << "Hello, my name is " << name << '.' << endl;
}

virtual void SayAge() final {


cout << "I am " << age << " years old." << endl;
}

Why is there an error?


To prevent a derived class from overriding a base class function, you
can use the key terms virtual and final. virtual goes in front of the
function declaration name while final goes behind. Adding these key
terms keeps you from overriding the functions in the Superhero class.
That is why you see the error. You’ll learn more about these key terms
in a future module.
Learning Objectives: Multilevel
Inheritance
Define multilevel inheritance

Create multilevel inheritance from a class with a base


class

Extend a class and override a function within multilevel


inheritance
Multiple Inheritance

Multiple Inheritance
Multiple inheritance is a condition where a class inherits from more than
one base class. C++ allows multiple inheritance for both associated and
unassociated base classes; however, for this particular section, we will only
go over multiple inheritance with associated base classes, meaning one
base class is derived from another base class.

Multilevel Inheritance
Multiple inheritance with associated base classes is called multilevel
inheritance. This is a condition where a class inherits from more than one
base class, but each base class is associated with each other. The image
below shows ClassC inheriting from ClassB, which in turn inherits from
ClassA. This is an example of multilevel inheritance.

.guides/img/inheritance/MultiLevelInheritance

The classes Carnivore and Dinosaur are already defined. Carnivore is the
base class for Dinosaur. Create the Tyrannosaurus class which is a derived
class of Dinosaur. The constructor for Tyrannosaurus takes a string and two
doubles and gets associated with the constructor from the Dinosaur class.

//add class definitions below this line

class Tyrannosaurus : public Dinosaur {


public:
Tyrannosaurus(string d, double s, double w) : Dinosaur(d,
s, w) {}
};

//add class definitions above this line


Instantiate a Tyrannosaurus object with the appropriate arguments. This t-
rex tiny is 12 meters tall, weighs 14 metric tons, and eats whatever it
wants. Print the size attribute to make sure inheritance is working as
expected.

//add code below this line

Tyrannosaurus tiny("whatever it wants", 12, 14);


cout << tiny.GetSize() << endl;

//add code above this line

challenge

Try these variations:


Print the weight attribute with cout << tiny.GetWeight() << endl;
Print the diet attribute with cout << tiny.GetDiet() << endl;
Extending & Overriding Functions

Extending a Class within Multilevel


Inheritance
Multilevel inheritance works just like single inheritance except there are
more than one derived class. Add the following code as class definitions in
the text editor.

//add class definitions below this line

class ClassC : public ClassB {


public:
void Bonjour() {
cout << "Bonjour" << endl;
}
};

//add class definitions above this line

Instantiate a ClassC object to call the Bonjour function. Then use the scope
resolution operator : to invoke the Hello function from both ClassB and
ClassA.

//add code below this line

ClassC c;
c.Bonjour();
c.ClassB::Hello();
c.ClassA::Hello();

//add code above this line


challenge

Try this variation:


Extend ClassC with the function AuRevoir that prints Au revoir.
Then call this function in main.
Solution

//add class definitions below this line

class ClassC : public ClassB {


public:
void Bonjour() {
cout << "Bonjour" << endl;
}

void AuRevoir() {
cout << "Au revoir" << endl;
}
};

//add class definitions above this line

int main() {

//add code below this line

ClassC c;
c.AuRevoir();

//add code above this line

return 0;

Overriding a Function within Multilevel


Inheritance
Like extending a class, overriding a function works the same in multilevel
inheritance as it does in single inheritance. Change ClassC so that it
overrides the Hello function.
//add class definitions below this line

class ClassC : public ClassB {


public:
void Hello() {
cout << "Hello from Class C" << endl;
}
};

//add class definitions above this line

Now replace the call to Bonjour with a call to Hello.

//add code below this line

ClassC c;
c.Hello();
c.ClassB::Hello();
c.ClassA::Hello();

//add code above this line

Notice how calling the Hello function automatically defaults to the function
within the object’s specified class. If you want to call the same function as
specified from within another base class, simply use the scope resolution
operator : as shown above.
Lab 1

Lab 1 - Making the Base Class and Derived Class


In this lab, we will be creating a base class called Line. Line has only one
attribute, int length, which is used in the class function DrawLine. DrawLine
takes an integer parameter and outputs * as many times as specified in
length. To retrieve length, we also have the getter function GetLength. Line
does not have a setter function.

//add class definitions below this line

class Line {
public:
Line(int l) {
length = l;
}

int GetLength() {
return length;
}

void DrawLine() {
for (int i = 0; i < length; i++) {
cout << '*';
}
cout << endl;
}

private:
int length;
};

//add class definitions above this line

To test our Line class, create its object and then call the DrawLine function
on it in main.
//add code below this line

Line line(10);
line.DrawLine();

//add code above this line

The output is a line drawn with 10 * symbols. Next, we will create a


derived class, Box, that inherits from Line.

Box has one attribute, int width, which will present the width of the Box
object. The Box constructor takes two parameters, one of which is presented
by width and the other is presented by length which is inherited from the
Line constructor. Box has two class functions, the getter GetWidth and
DrawBox. Notice how inheritance enables us to borrow functions and
attributes from the base class to further extend the derived class.

DrawBox utilizes the width attribute to tell the system how many times to
call DrawLine. The end result is a draw of a “box” that is created from
multiple “lines”.
//add class definitions below this line

class Line {
public:
Line(int l) {
length = l;
}

int GetLength() {
return length;
}

void DrawLine() {
for (int i = 0; i < length; i++) {
cout << '*';
}
cout << endl;
}

private:
int length;
};

class Box : public Line {


public:
Box(int l, int w) : Line(l) {
width = w;
}

int GetWidth() {
return width;
}

void DrawBox() {
for (int i = 0; i < width; i++) {
DrawLine();
}
}

private:
int width;
};

//add class definitions above this line

Run the code below in main to see the output.


//add code below this line

Box box(10, 10);


box.DrawBox();

//add code above this line

challenge

Try these variations:


Change Box box(10, 10); to Box box(3, 6);.
Change Box box(3, 6); to Box box(8, 4);.
Lab 2

Lab 2 - Applying Multilevel Inheritance


Previously in Lab 1, we created the base class Line and the derived class
Box. In this lab, we will create another derived class that inherits from Box
directly and Line indirectly. This concept of a derived class inheriting from
another derived class is called multilevel inheritance.

Given Code

#include <iostream>
using namespace std;

//add class definitions below this line

class Line {
public:
Line(int l) {
length = l;
}

int GetLength() {
return length;
}

void DrawLine() {
for (int i = 0; i < length; i++) {
cout << '*';
}
cout << endl;
}

private:
int length;
};

class Box : public Line {


public:
Box(int l, int w) : Line(l) {
width = w;
}
int GetWidth() {
return width;
}

void DrawBox() {
for (int i = 0; i < width; i++) {
DrawLine();
}
}

private:
int width;
};

//add class definitions above this line

int main() {

//add code below this line

//add code above this line

return 0;

Our new derived class is called Pattern. This class builds off of Box and Line
by utilizing their getter functions GetLength and GetWidth. First we need to
build the Pattern constructor which inherits the Box constructor exactly.
Then we will create a new function called DrawPattern that will output a
modified “box” with a pattern. Note that Pattern does not have any private
members. It is simply an extension of the Box class.

//add class definitions below this line

class Line {
public:
Line(int l) {
length = l;
}

int GetLength() {
return length;
}

void DrawLine() {
for (int i = 0; i < length; i++) {
cout << '*';
}
cout << endl;
}

private:
int length;
};

class Box : public Line {


public:
Box(int l, int w) : Line(l) {
width = w;
}

int GetWidth() {
return width;
}

void DrawBox() {
for (int i = 0; i < width; i++) {
DrawLine();
}
}

private:
int width;
};

class Pattern : public Box {


public:
Pattern(int l, int w) : Box(l, w) {}

void DrawPattern() {
for (int i = 0; i < GetLength(); i++) {
if (i % 2 == 0) {
for (int j = 0; j < GetWidth(); j++) {
if ( (j % 2 == 0) ) {
cout << '*';
}
else {
cout << ' ';
}
}
cout << endl;
}
if (i % 2 == 1) {
for (int j = 0; j < GetWidth(); j++) {
if ( (j % 2 == 0) ) {
cout << ' ';
}
else {
cout << '*';
}
}
cout << endl;
}
}
}
};

//add class definitions above this line

Rather than create a “box” of “lines”, a “pattern” is created by specifying


certain indices to output * while others output a white space (' '). In
particular, this is the pattern (note the pattern starts at index 0 for both
rows and columns):

even row + even column = *


even row + odd column = ' '
odd row +even column = ' '
odd row + odd column = *

Try the following code in main to see the output.

//add code below this line

Pattern pattern(10, 10);


pattern.DrawPattern();

//add code above this line


challenge

Try these variations:


Change Pattern pattern(10, 10); to Pattern pattern(3, 6);.
Change Pattern pattern(3, 6); to Pattern pattern(8, 20);.
Lab Challenge

Problem

In the IDE to the left, the class MP3 is already defined. Complete the class
Podcast that inherits from MP3. This class should do the following things:
* Inherit the constructor such that Podcast has the following attributes:
* title - a string that is the title of the episode
* length - an integer that has the length of the podcast in seconds
* genre - a string that is the genre of the podcast
* name - a string that is the name of the podcast
* date - a string that represents when the podcast was released to the public
* DO NOT EDIT the specified code or you might not receive credit for your
work!

Hint: Connecting the Constructors

Note that a few of the attributes are present in both the MP3 and Podcast
classes. To connect their constructors, you can use the same parameter
values for the attributes that are the same, then use different parameter
values for the attributes that are different. Finally, set the attributes to the
parameters appropriately for the ones that are different. For example:

Podcast(string t, int l, string g, string n, string d) : MP3(t,


l, g, n, d) {
name = n;
date = d;
}

Given Code

#include <iostream>
using namespace std;

//DO NOT EDIT code below this line

class MP3 {
public:
MP3(string t, int l, string g, string al, string ar) {
title = t;
album = al;
length = l;
genre = g;
artist = ar;
}

string GetTitle() {
return title;
}

void SetTitle(string new_title) {


title = new_title;
}

int GetLength() {
return length;
}

void SetLength(int new_length) {


length = new_length;
}

string GetGenre() {
return genre;
}

void SetGenre(string new_genre) {


genre = new_genre;
}

string GetAlbum() {
return album;
}

void SetAlbum(string new_album) {


album = new_album;
}

string GetArtist() {
return artist;
}

void SetArtist(string new_artist) {


artist = new_artist;
}

private:
string title;
int length;
string genre;
string album;
string artist;
};

//DO NOT EDIT code above this line

//add class definitions below this line

//DO NOT EDIT///////////////////


class Podcast : public MP3 { //
////////////////////////////////

//add class definitions above this line

int main() {

//DO NOT EDIT code below this line

Podcast p("Hollywood Black List", 1460, "economics", "Planet


Money", "10 July 2020");
p.DisplayTitle();
p.DisplayLength();
p.DisplayGenre();
p.DisplayName();
p.DisplayDate();

//DO NOT EDIT code above this line

return 0;

Expected Output

The title is Hollywood Black List


The length is 1460
The genre is economics
The name is Planet Money
The date is 10 July 2020
Testing Your Code
Use the button below to test your code before submitting it for evaluation.
Learning Objectives:
Polymorphism
Define polymorphism

Explain how function overriding is an example of


polymorphism

Overload a function

Override a function

Use an abstract function as a form of polymorphism


Function Overriding

What is Polymorphism?
Polymorphism is a concept in object-oriented programming in which a
single interface takes different forms (polymorphism means “many
forms”). Often this means similar operations are grouped together with the
same name. However, these operations with the same name will produce
different results. You have already encountered a few examples of
polymorphism. Enter the following code into the IDE.

//add code below this line

int a = 5;
int b = 10;
cout << (a + b) << endl;

string c = "5";
string d = "10";
cout << (c + d) << endl;

bool e = true;
bool f = false;
cout << (e + f) << endl;

//add code above this line

Notice how the plus operator (+) can add together two numbers,
concatenate two strings, and add two booleans. You have a single interface
(the plus operator) taking different forms — one that works with integers,
another that works with strings, and even one that works with booleans.
This is an example of polymorphism.

Operator Overloading
Because the plus operator can work with different forms, we can say that it
is overloaded. C++ overloads this operator by default. However, a user
cannot manually overload an operator.
challenge

Try these variations:


Change your code to look like this:

//add code below this line

int a = 5;
string b = "true";
cout << (a + b) << endl;

//add code above this line

Why is there an error?


Polymorphism allows C++ to use the plus operator with different data
types, but that does not mean that the plus operator can be used with
all data types. The example above causes an error message because the
plus operator cannot be used with an integer and a string. There are
limits to polymorphism.

Change string b = "true"; in the code above to bool b = true;.

Why does the code above work?


Remember that a boolean value of true is equivalent to an integer
value of 1. This is why it is possible to add boolean and integer values
together.

Function Overriding
Function overriding is another example of polymorphism that you have
already seen. Overriding a function means that you have two functions
with the same name, but they perform different tasks. Again you see a
single interface (the function name) being used with different forms (the
base class and the derived class). Create the following classes.
//add class definitions below this line

class Alpha {
public:
void Show() {
cout << "I am from class Alpha" << endl;
}
};

class Bravo: public Alpha {


public:
void Show() {
cout << "I am from class Bravo" << endl;
}
};

//add class definitions above this line

Then instantiate an Alpha object and call the Show function.

//add code below this line

Alpha test_object;
test_object.Show();

//add code above this line

As expected, the script prints I am from class Alpha. Now change the line
of code in which you instantiate the object test_object to a Bravo object
like below. Make no other changes and run the code again.

Bravo test_object;

Now the script prints I am from class Bravo. The function call did not
change, but the output did. A single interface (the Show function) works
with multiple forms (the Alpha and Bravo data types). This is why function
overriding is an example of polymorphism.

challenge

Try this variation:


Create and overload the function Hello that prints Hello from
Alpha and Hello from Bravo to the Alpha and Bravo classes
respectively. Then test the function on both class types by calling it
on their respective objects.

Solution

//add class definitions below this line

class Alpha {
public:
void Show() {
cout << "I am from class Alpha" << endl;
}

void Hello() {
cout << "Hello from Alpha" << endl;
}
};

class Bravo: public Alpha {


public:
void Show() {
cout << "I am from class Bravo" << endl;
}

void Hello() {
cout << "Hello from Bravo" << endl;
}
};

//add class definitions above this line

int main() {

//add code below this line

Alpha test_object; //Then test with Bravo test_object


test_object.Hello();

//add code above this line

return 0;

}
Function Overloading

Function Overloading
Function overloading is another example of polymorphism. Function
overloading occurs when you have a single function name that can take
different sets of parameters. Imagine you want to write the function Sum
that can sum up to three numbers. The math involved with three
parameters is slightly different than two parameters, which is different
from 1 parameter, etc. Traditionally, if you declare a function that takes
three parameters but only pass two, C++ will throw an error message.
Instead, let’s create a class that has two Sum functions; one with two
parameters and another with three parameters.

//add class definitions below this line

class TestClass {
public:
int Sum(int n1, int n2, int n3) {
return n1 + n2 + n3;
}

int Sum(int n1, int n2) {


return n1 + n2;
}
};

//add class definitions above this line

Create an object of type TestClass and call both versions of the Sum
function. Be sure you are passing three arguments for one function and
two arguments for the other.

//add code below this line

TestClass tc;
cout << tc.Sum(1, 2, 3) << endl;
cout << tc.Sum(1, 2) << endl;

//add code above this line


C++ looks at the number and types of arguments and, as long there is a
matching function definition, runs the code without an error. Defining the
same function with different sets of arguments is called overloading. It is
also an example of polymorphism.

challenge

Try this variation:


Continue to overload the Sum function such that it can take up to
five numbers as parameters (which means 4 functions total). Be
sure to test all possible function calls in main.

Solution
//add class definitions below this line

class TestClass {
public:
int Sum(int n1, int n2, int n3, int n4, int n5) {
return n1 + n2 + n3 + n4 + n5;
}

int Sum(int n1, int n2, int n3, int n4) {


return n1 + n2 + n3 + n4;
}

int Sum(int n1, int n2, int n3) {


return n1 + n2 + n3;
}

int Sum(int n1, int n2) {


return n1 + n2;
}
};

//add class definitions above this line

int main() {

//add code below this line

TestClass tc;
cout << tc.Sum(1, 2, 3, 4, 5) << endl;
cout << tc.Sum(1, 2, 3, 4) << endl;
cout << tc.Sum(1, 2, 3) << endl;
cout << tc.Sum(1, 2) << endl;

//add code above this line

return 0;

Overloading the Constructor


C++ will also allow you to overload the constructor so that objects are
instantiated in a variety of ways. The Person class has a default constructor
(no arguments) and a constructor with three arguments.

//add class definitions below this line

class Person {
public:
Person() {}

Person(string na, int nu, string s) {


name = na;
number = nu;
street = s;
}

string Info() {
return (name + " lives at " + to_string(number) + ' ' +
street + '.');
}

private:
string name;
int number;
string street;
};

//add class definitions above this line

When you create a Person object with no arguments, the Info function still
works. However, the information that is printed may look jarring since C++
will use left-over “junk” memory data to fill in for variables that are not
initialized. You can also instantiate an object with three arguments. Like
function overloading, constructor overloading is a form of polymorphism.

//add code below this line

Person p1;
Person p2("Calvin", 37, "Main Street");
cout << p1.Info() << endl;
cout << p2.Info() << endl;

//add code above this line

Random Values
Unlike other programming languages, C++ does not automatically initialize
variables that are not initialized by the user. This is done to preserve
memory. Thus, you might get “randomly” generated data for certain
uninitialized variables. Run the code several more times and you’ll notice
different values will be printed.

challenge

Try these variations:


Comment out both of the constructors.

//add class definitions below this line

class Person {
public:
//Person() {}

//Person(string na, int nu, string s) {


//name = na;
//number = nu;
//street = s;
//}

string Info() {
return (name + " lives at " + to_string(number) + " "
+ street + ".");
}

private:
string name;
int number;
string street;
};

//add class definitions above this line

Instantiate a Person object and call the Info function.


//add code below this line

Person p1;
//Person p2("Calvin", 37, "Main Street");
cout << p1.Info() << endl;
//cout << p2.Info() << endl;

//add code above this line

Why does this work?


When you do not declare a constructor, C++ will use the default
constructor and give each of the attributes their default value.

Uncomment only the constructor with three arguments.

//add class definitions below this line

class Person {
public:
//Person() {}

Person(string na, int nu, string s) {


name = na;
number = nu;
street = s;
}

string Info() {
return (name + " lives at " + to_string(number) + " "
+ street + ".");
}

private:
string name;
int number;
string street;
};

//add class definitions above this line

Do not make any changes to the object instantiation in main.

Why is there an error?


C++ automatically uses the default constructor when there are no
constructors defined. If, however, a constructor exists, the object that
gets instantiated must contain the same number of arguments as
specified by that constructor’s parameters. Otherwise, an error will be
produced.
Abstract Functions

Abstract Classes
Another form of polymorphism in C++ involves abstract functions. These
functions, however, require knowledge of abstract classes. So before we
continue the discussion on polymorphism, we need to first talk about
abstract classes.

Concrete Classes
Any class that is not an abstract class is considered to be a concrete class.
You do not need to use a keyword to indicate that a class is concrete.

A defining characteristic of an abstract class is that an abstract class has at


least one abstract, or pure virtual, function. An abstract function is a
function that is defined as being equal to 0 in the base class, but is expected
to be redefined in the derived class.

Abstract (Pure Virtual) Functions


When will there be a need for abstract functions? Abstract functions are
used when the derived classes are expected to use a particular abstract
function from the base class differently. Let’s take a look at a classic
example of the Shape class.

//add class definitions below this line

class Shape {
public:
virtual double Area() = 0;
};

//add class definitions above this line

As seen in the above code, the virtual function Area is defined as being
equal to 0. We do this because we expect classes that are derived from
Shape to have Area functions that behave differently. For example,
calculating the area of a Triangle object is different from calculating the
area of a Rectangle object. Thus, we define Area as an abstract function in
the base class Shape. Note that when you redefine abstract functions in the
derived classes, you do not include the virtual keyword nor assign the
function to 0. Let’s define our classes further.

//add class definitions below this line

class Shape {
public:
virtual double Area() = 0;

double GetBase() {
return base;
}

void SetBase(double new_base) {


base = new_base;
}

double GetHeight() {
return height;
}

void SetHeight(double new_height) {


height = new_height;
}

protected:
double base;
double height;
};

class Triangle : public Shape {


public:
Triangle(double b, double h) {
base = b;
height = h;
}

double Area() {
return base * height / 2;
}
};

class Rectangle : public Shape {


public:
Rectangle(double b, double h) {
base = b;
height = h;
}

double Area() {
return base * height;
}
};

//add class definitions above this line

You can see above that two classes are derived from Shape, the Triangle
class and the Rectangle class. Also notice how Shape contains additional
getters and setters as well as protected attributes. We encapsulate them as
protected in order for our derived classes to access them. This way, we
don’t have to declare additional attributes in Triangle and Rectangle.

Next, let’s test our code in main.

//add code below this line

Triangle t(4, 4);


cout << t.Area() << endl;
Rectangle r(4, 4);
cout << r.Area() << endl;

//add code above this line

As expected, the code returns the correct calculations for the Triangle and
Rectangle objects.
challenge

Try this variation:


Change cout << t.Area() << endl; to cout << t.Shape::Area() <<
endl;
Replace the entire code in main with

//add code below this line

Shape s;

//add code above this line

Why are there errors?


You cannot create an object of an abstract class nor can you call an
abstract function. This is why using encapsulation and inheritance is
important in redefining functions so that they can be used in derived
classes. Because abstract functions get redefined and used differently,
they are considered to be a concept of polymorphism.
Lab 1

Lab 1
This lab will focus on using polymorphism while interacting with a contact
list. There will be a main class Contacts that controls the “view” the user
sees and responds to user input. The contact information (personal and
work) will be an instance of the Information class.

The Contacts Class


This lab is built around the Contacts class, which has four attributes:
* view - This string attribute controls what the user sees. When the value
for view changes, the information changes. There are four different views.
* List view - Shows the list of all of the contacts.
* Information view - Shows the work and personal information for a
particular contact.
* Add view - Add information for a new contact.
* Quit view - Leave a message for the user and then end the program.
* names - Vector of strings that stores the names of each person in the
contact list.
* titles - Vector of strings that stores the titles for each person in the
contact list.
* workPhoneNumbers - Vector of strings that stores the work phone numbers
for each person in the contact list.
* workEmails - Vector of strings that stores the work email addresses for
each person in the contact list.
* personalPhoneNumbers - Vector of strings that stores the personal phone
numbers for each person in the contact list.
* personalEmails - Vector of strings that stores the personal email addresses
for each person in the contact list.

choice - This string attribute represents input from the user and is used
to change view.
index - This integer attribute keeps track of the particular contact whose
information is to be displayed.
length - This integer attribute keeps track of the length of the above
vectors.

To put polymorphism into practice, we are going to create the abstract class
Information. This will have the pure abstract functions DisplayInfo and
AddInfo. The Contacts class inherits from Information, and therefore it
must override the DisplayInfo and AddInfo functions.

//add class definitions below this line

class Information {
public:
virtual void DisplayInfo() = 0;
virtual void AddInfo() = 0;
};

class Contacts : public Information {


public:
void DisplayInfo() {

void AddInfo() {

}
};

//add class definitions above this line

Add the attributes and constructor for the Contacts class. For testing
purposes, set view to "quit". We will change this to a more appropriate
value later on. The other attributes do not need a value when instantiating
the object. Instantiate each vector attribute, set choice to an empty string,
and set index and length to 0. Additionally, add the Display function, which
we will go in more detail later on.

//add class definitions below this line

class Information {
public:
virtual void DisplayInfo() = 0;
virtual void AddInfo() = 0;
};

class Contacts : public Information {


public:
Contacts() {
view = "quit";
names = {};
titles = {};
workPhoneNumbers = {};
workEmails = {};
personalPhoneNumbers = {};
personalEmails = {};
choice = "";
index = 0;
length = 0;
}

void DisplayInfo() {

void AddInfo() {

void Display() {

private:
string view;
vector<string> names;
vector<string> titles;
vector<string> workPhoneNumbers;
vector<string> workEmails;
vector<string> personalPhoneNumbers;
vector<string> personalEmails;
string choice;
int index;
int length;
};

//add class definitions above this line

The Display Function


The Display function is designed to be a loop that runs until the user tells
the program to end. The function checks the value of the view attribute and
calls the appropriate function that displays the information for each view.
Since the loop is while (true), be sure to include a break statement
otherwise the loop would never stop (C++ would eventually stop the
program with an error message). Fill out the Display function like below.
void Display() {
while (true) {
if (view==("list")) {
}
else if (view==("info")) {
DisplayInfo();
}
else if (view==("add")) {
cout << " " << endl;
AddInfo();
}
else if (view==("quit")) {
cout << ("\nClosing the contact list...\n");
break;
}
}
}

Starting the Other Functions


The Display function calls three other functions; DisplayInfo and AddInfo
have already been declared. Trying to test the code would cause your
program to crash as the ShowList function has not yet been defined. Create
another empty function ShowList just as was done with DisplayInfo and
AddInfo. We will come back later and add working code to each function.
However, to get your code to run we must provide a definition for virtual
functions in your base class.

void DisplayInfo() {

void AddInfo() {

void ShowList() {

Testing Your Code


Before moving on to the next part of the script, we want to check that our
code is working. To do that, instantiate a Contacts object and call the
Display function.

//add code below this line

Contacts contacts;
contacts.Display();

//add code above this line

Run your program. Because the view attribute is "quit", the script should
immediately display a message and then stop. Your output should look
something like this.

.guides/img/Polymorphism/output_lab1_updated

Code

Your code should look like this:

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

class Information {
public:
virtual void DisplayInfo() = 0;
virtual void AddInfo() = 0;
};

class Contacts : public Information {


public:
Contacts() {
view = "quit";
names = {};
titles = {};
workPhoneNumbers = {};
workEmails = {};
personalPhoneNumbers = {};
personalEmails = {};
choice = "";
index = 0;
length = 0;
}
void DisplayInfo() {

void AddInfo() {

void ShowList() {

void Display() {
while (true) {
if (view==("list")) {
ShowList();
}
else if (view==("info")) {
DisplayInfo();
}
else if (view==("add")) {
cout<< " "<<endl;
AddInfo();
}
else if (view==("quit")) {
cout<< ("\nClosing the contact list...\n");
break;
}
}
}

private:
string view;
vector<string> names;
vector<string> titles;
vector<string> workPhoneNumbers;
vector<string> workEmails;
vector<string> personalPhoneNumbers;
vector<string> personalEmails;
string choice;
int index;
int length;
};

int main(){
//add code below this line

Contacts contacts;
contacts.Display();

//add code above this line

return 0;
}
Lab 2

Adding a Contact
The first thing we need to do is to change the default view when a Contacts
object is instantiated. The list view should be the first view shown to the
user. Change the value for view from "quit" to "list" in the constructor.
The rest of the constructor should not be changed.

Code from Lab 1

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

//add class definitions below this line

class Information {
public:
virtual void DisplayInfo() = 0;
virtual void AddInfo() = 0;
};

class Contacts : public Information {


public:
Contacts() {
view = "quit";
names = {};
titles = {};
workPhoneNumbers = {};
workEmails = {};
personalPhoneNumbers = {};
personalEmails = {};
choice = "";
index = 0;
length = 0;
}

void DisplayInfo() {

}
void AddInfo() {

void ShowList() {

void Display() {
while (true) {
if (view==("list")) {
ShowList();
}
else if (view==("info")) {
DisplayInfo();
}
else if (view==("add")) {
cout<< " "<<endl;
AddInfo();
}
else if (view==("quit")) {
cout << ("\nClosing the contact list...\n");
break;
}
}
}

private:
string view;
vector<string> names;
vector<string> titles;
vector<string> workPhoneNumbers;
vector<string> workEmails;
vector<string> personalPhoneNumbers;
vector<string> personalEmails;
string choice;
int index;
int length;
};

//add class definitions above this line

int main(){

//add code below this line

Contacts contacts;
contacts.Display();
//add code above this line

return 0;

public:
Contacts() {
view = "list";
// rest of the constructor remains unchanged
}

Next we want to modify the ShowList function to show the list of people in
the contact list. There are two possible states for the list view: the list is
empty or there are contacts in the list. When the list is empty, the user will
be provided with the choice to add a contact or quit the program. Use a
conditional to represent these two states. For now, set view to "quit" in the
else branch.

The print statement is to add a blank line for legibility. We also need a cin
object to collect input from the user. If length is 0, then present the user
with a choice. Store their input in choice. The .tolower() function will
convert the user choice to a lowercase letter. This will make comparisons
easier. Remember, C++ is case sensitive; q and Q are not the same. By
forcing all input to lowercase, we only need to test for the lowercase letter.
The ShowList function ends by calling another function to handle the user’s
choice.

void ShowList() {
cout << endl;
char sc;
if (length == 0) {
cout << ("(A)dd a new contact \n(Q)uit \n> ");
cin >> sc;
choice = putchar(tolower(sc));
}
else {
view = "quit";
}
HandleChoice();
}

Handling User Choices


Every time the user makes a choice, we want to evaluate that choice and
perform the appropriate action. In this case, the user can choose between
adding a contact or quitting the program. Notice that view only changes to
"add" if "a" is entered and we are in list view. We only want to add new
contacts from the list view.

void HandleChoice() {
if (choice==("q")) {
view = "quit";
}
else if (choice==("a") && view==("list")) {
view = "add";
}
}

Adding a Contact
When the user enters "a", we need to create a new contact. To do this, we
are going to modify the AddInfo function. Use cout and the getline function
to ask the user to enter the name, personal phone number, personal email,
work title, work phone number, and work email, and then temporarily
store that information. Each piece of information should go into the
corresponding vector attribute after. Notice that we include cin >> ws
within getline because by doing so, we are able to consume the whitespace
ws. Once the information has been added, increase the length attribute and
revert back to the list view.
void AddInfo() {
cout << endl;
string sc2;
cout << ("Enter their name: ");
getline(cin >> ws, sc2);
string name = sc2;
names.push_back(name);

cout << ("Enter their personal phone number: ");


getline(cin >> ws, sc2);
string personalPhone = sc2;
personalPhoneNumbers.push_back(personalPhone);

cout << ("Enter their personal email: ");


getline(cin >> ws, sc2);
string personalEmail = sc2;
personalEmails.push_back(personalEmail);

cout << ("Enter their work title: ");


getline(cin >> ws, sc2);
string title = sc2;
titles.push_back(title);

cout << ("Enter their work phone number: ");


getline(cin >> ws, sc2);
string workPhone = sc2;
workPhoneNumbers.push_back(workPhone);

cout << ("Enter their work email: ");


getline(cin >> ws, sc2);
string workEmail = sc2;
workEmails.push_back(workEmail);
length++;
view = "list";
}

Testing Your Code


Before moving on to the next part of the program, we want to check that
our code is adding a contact to the list. To do that, we need to create a getter
for the length attribute.
int GetLength() {
return length;
}

Now call print the result from the GetLength function.

//add code below this line

Contacts contacts;
contacts.Display();
cout<< contacts.GetLength()<< endl;

//add code above this line

Run the program and enter a when prompted, then add the following
contact:

Rachel Kim
555 123-4567
rachel_k@personalMail.com
Senior Software Engineer
555 890-1234
rkim@workMail.com

If everything worked properly, your program should print 1 in the terminal


as there is one person in our contact list.

Code

Your code should look like this:

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

//add class definitions below this line

class Information {
public:
virtual void DisplayInfo() = 0;
virtual void AddInfo() = 0;
};

class Contacts : public Information {


public:
Contacts() {
view = "list";
names = {};
titles = {};
workPhoneNumbers = {};
workEmails = {};
personalPhoneNumbers = {};
personalEmails = {};
choice = "";
index = 0;
length = 0;
}

void DisplayInfo() {

void AddInfo() {
cout << endl;
string sc2;
cout << ("Enter their name: ");
getline(cin >> ws, sc2);
string name = sc2;
names.push_back(name);

cout << ("Enter their personal phone number: ");


getline(cin >> ws, sc2);
string personalPhone = sc2;
personalPhoneNumbers.push_back(personalPhone);

cout << ("Enter their personal email: ");


getline(cin >> ws, sc2);
string personalEmail = sc2;
personalEmails.push_back(personalEmail);

cout << ("Enter their work title: ");


getline(cin >> ws, sc2);
string title = sc2;
titles.push_back(title);

cout << ("Enter their work phone number: ");


getline(cin >> ws, sc2);
string workPhone = sc2;
workPhoneNumbers.push_back(workPhone);

cout << ("Enter their work email: ");


getline(cin >> ws, sc2);
string workEmail = sc2;
workEmails.push_back(workEmail);
length++;
view = "list";
}

int GetLength() {
return length;
}

void HandleChoice() {
if (choice==("q")) {
view = "quit";
}
else if (choice==("a") && view==("list")) {
view = "add";
}
}

void ShowList() {
cout << endl;
char sc;
if (length == 0) {
cout << ("(A)dd a new contact \n(Q)uit \n> ");
cin >> sc;
choice = putchar(tolower(sc));
}
else {
view = "quit";
}
HandleChoice();
}

void Display() {
while (true) {
if (view==("list")) {
ShowList();
}
else if (view==("info")) {
DisplayInfo();
}
else if (view==("add")) {
cout << endl;
AddInfo();
}
else if (view==("quit")) {
cout << ("\nClosing the contact list...\n");
break;
}
}
}

private:
string view;
vector<string> names;
vector<string> titles;
vector<string> workPhoneNumbers;
vector<string> workEmails;
vector<string> personalPhoneNumbers;
vector<string> personalEmails;
string choice;
int index;
int length;
};

//add class definitions above this line

int main(){

//add code below this line

Contacts contacts;
contacts.Display();
cout << contacts.GetLength() << endl;

//add code above this line

return 0;

}
Lab 3

Displaying the List View


Now that we can add a contact to the list, we will want to show all of the
contacts in the list.

Code from Lab 2

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

//add class definitions below this line

class Information {
public:
virtual void DisplayInfo() = 0;
virtual void AddInfo() = 0;
};

class Contacts : public Information {


public:
Contacts() {
view = "list";
names = {};
titles = {};
workPhoneNumbers = {};
workEmails = {};
personalPhoneNumbers = {};
personalEmails = {};
choice = "";
index = 0;
length = 0;
}

void DisplayInfo() {

void AddInfo() {
cout << endl;
string sc2;
cout << ("Enter their name: ");
getline(cin >> ws, sc2);
string name = sc2;
names.push_back(name);

cout << ("Enter their personal phone number: ");


getline(cin >> ws, sc2);
string personalPhone = sc2;
personalPhoneNumbers.push_back(personalPhone);

cout << ("Enter their personal email: ");


getline(cin >> ws, sc2);
string personalEmail = sc2;
personalEmails.push_back(personalEmail);

cout << ("Enter their work title: ");


getline(cin >> ws, sc2);
string title = sc2;
titles.push_back(title);

cout << ("Enter their work phone number: ");


getline(cin >> ws, sc2);
string workPhone = sc2;
workPhoneNumbers.push_back(workPhone);

cout << ("Enter their work email: ");


getline(cin >> ws, sc2);
string workEmail = sc2;
workEmails.push_back(workEmail);
length++;
view = "list";
}

int GetLength() {
return length;
}

void HandleChoice() {
if (choice==("q")) {
view = "quit";
}
else if (choice==("a") && view==("list")) {
view = "add";
}
}
void ShowList() {
cout << endl;
char sc;
if (length == 0) {
cout << ("(A)dd a new contact \n(Q)uit \n> ");
cin >> sc;
choice = putchar(tolower(sc));
}
else {
view = "quit";
}
HandleChoice();
}

void Display() {
while (true) {
if (view==("list")) {
ShowList();
}
else if (view==("info")) {
DisplayInfo();
}
else if (view==("add")) {
cout << endl;
AddInfo();
}
else if (view==("quit")) {
cout << ("\nClosing the contact list...\n");
break;
}
}
}

private:
string view;
vector<string> names;
vector<string> titles;
vector<string> workPhoneNumbers;
vector<string> workEmails;
vector<string> personalPhoneNumbers;
vector<string> personalEmails;
string choice;
int index;
int length;
};

//add class definitions above this line


int main(){

//add code below this line

Contacts contacts;
contacts.Display();
cout << contacts.GetLength() << endl;

//add code above this line

return 0;

But before doing that, we need to remove the print statement at the end of
the script. The final two lines of code should look like this:

//add code below this line

Contacts contacts;
contacts.Display();

//add code above this line

We are going to iterate through the list of contacts and print the name. To
help users select a contact, a number will appear before each name. This
way, the user types the number, and the contact’s information appears.
Previously, the cin object stored the user input as a char, but since we want
the user to be able to input numbers now, we have to change the input sc
to a string instead. Then we create a loop in which sc gets iterated and each
character will be converted into lowercase and added to the string choice.
In the else branch function, create a for loop to go from 0 to length. We
want a number followed by the name. The numbers should start at 1, so
print the loop index plus 1 followed by the element from the names vector.
After displaying the list of names, ask the user to make a selection. Entering
a number will show all of the information about a contact. After
HandleChoice is called, be sure to set choice back to an empty string;
otherwise, the user’s input will continue to be stored in choice and
negatively affect the acceptable input requirement.
void ShowList() {
cout << endl;
string sc;
if (length == 0) {
cout << ("(A)dd a new contact \n(Q)uit \n> ");
cin >> sc;
for (char c : sc) {
choice += (tolower(c));
}
}
else {
for (int i = 0; i < length; i++) {
cout << (i + 1 ) << ") " << names.at(i) << endl;
}
cout << ("\n(#) Select a name \n(A)dd a new
contact\n(Q)uit \n> ");
cin >> sc;
for (char c : sc) {
choice += (tolower(c));
}
}
HandleChoice();
choice = "";
}

Handling Numeric Input


Add an else if branch to the HandleChoice function that asks if the user
input is numeric (remember, the cin object now stores user input as a
string) and if the user is in the list view. If yes, then convert the user input
to an integer, subtract 1, and store it in the variable num. Remember, we
added one to the loop index in the ShowList function. If the user made a
mistake in entering the number, the script will crash if you try to access an
index that is outside of the vector. So we need to verify that the number is
between 0 and length. Finally, set index to num and set view to "info".
void HandleChoice() {
if (choice==("q")) {
view = "quit";
}
else if (choice==("a") && view==("list")) {
view = "add";
}
else if (IsNumeric(choice) && view==("list")) {
int num = (stoi(choice) - 1);
if (num >= 0 && num < length) {
index = num;
view = "info";
}
}
}

We need to create the boolean helper function IsNumeric which takes the
user input and determines if they entered a number. First see if the user
input (a string) is empty. Return false if either condition is true. Then try to
convert the string to an integer. If this works, return true. If C++ throws an
exception because the string cannot be converted to an integer, then return
false. Since IsNumeric is a helper function, you should encapsulate it as
private.

bool IsNumeric(string s) {
int value;

if (s == "") {
return false;
}

try {
value = stoi(s);
return true;
}
catch (runtime_error& e) {
return false;
}
}

Testing Your Code


Before moving on to the next part of the program, we want to check that
our code is displaying all of the contacts in the list. To do that, enter two
different contacts. The first one is:
John Calvin
555 111-2222
john.calvin@email.net
Philosopher
555 333-4444
jcalvin@work.org

You should see a list that looks like this:

.guides/img/Polymorphism/lab3_pic1

Now add a second contact to the list:

Thomas Hobbes
555 666-7777
t_hobbes@email.net
Philosopher
555 888-9999
tom_hobbes@work.org

Your program should now show the following output:

.guides/img/Polymorphism/lab3_pic2

Code

Your code should look like this:

#include <iostream>
#include <vector>
using namespace std;
//add class definitions below this line

class Information {
public:
virtual void DisplayInfo() = 0;
virtual void AddInfo() = 0;
};

class Contacts : public Information {


public:
Contacts() {
view = "list";
names = {};
titles = {};
workPhoneNumbers = {};
workEmails = {};
personalPhoneNumbers = {};
personalEmails = {};
choice = "";
index = 0;
length = 0;
}

void DisplayInfo() {

void AddInfo() {
cout << endl;
string sc2;
cout << ("Enter their name: ");
getline(cin >> ws, sc2);
string name = sc2;
names.push_back(name);

cout << ("Enter their personal phone number: ");


getline(cin >> ws, sc2);
string personalPhone = sc2;
personalPhoneNumbers.push_back(personalPhone);

cout << ("Enter their personal email: ");


getline(cin >> ws, sc2);
string personalEmail = sc2;
personalEmails.push_back(personalEmail);

cout << ("Enter their work title: ");


getline(cin >> ws, sc2);
string title = sc2;
titles.push_back(title);

cout << ("Enter their work phone number: ");


getline(cin >> ws, sc2);
string workPhone = sc2;
workPhoneNumbers.push_back(workPhone);

cout << ("Enter their work email: ");


getline(cin >> ws, sc2);
string workEmail = sc2;
workEmails.push_back(workEmail);
length++;
view = "list";
}

int GetLength() {
return length;
}

void HandleChoice() {
if (choice==("q")) {
view = "quit";
}
else if (choice==("a") && view==("list")) {
view = "add";
}
else if (IsNumeric(choice) && view==("list")) {
int num = (stoi(choice) - 1);
if (num >= 0 && num < length) {
index = num;
view = "info";
}
}
}

void ShowList() {
cout << endl;
string sc;
if (length == 0) {
cout << ("(A)dd a new contact \n(Q)uit \n> ");
cin >> sc;
for (char c : sc) {
choice += (tolower(c));
}
}
else {
for (int i = 0; i < length; i++) {
cout << (i + 1 ) << ") " << names.at(i) << endl;
}
cout << ("\n(#) Select a name \n(A)dd a new
contact\n(Q)uit \n> ");
cin >> sc;
for (char c : sc) {
choice += (tolower(c));
}
}
HandleChoice();
choice = "";
}

void Display() {
while (true) {
if (view=="list") {
ShowList();
}
else if (view==("info")) {
DisplayInfo();
}
else if (view==("add")) {
cout << endl;
AddInfo();
}
else if (view==("quit")) {
cout << ("\nClosing the contact list...\n");
break;
}
}
}

private:
string view;
vector<string> names;
vector<string> titles;
vector<string> workPhoneNumbers;
vector<string> workEmails;
vector<string> personalPhoneNumbers;
vector<string> personalEmails;
string choice;
int index;
int length;

bool IsNumeric(string s) {
int value;

if (s == "") {
return false;
}

try {
value = stoi(s);
return true;
}
catch (runtime_error& e) {
return false;
}
}
};

//add class definitions above this line

int main(){

//add code below this line

Contacts contacts;
contacts.Display();

//add code above this line

return 0;

}
Lab 4

Displaying Contact Info


The next step is to display the contact information for a selected contact.

Code from Lab 3

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

//add class definitions below this line

class Information {
public:
virtual void DisplayInfo() = 0;
virtual void AddInfo() = 0;
};

class Contacts : public Information {


public:
Contacts() {
view = "list";
names = {};
titles = {};
workPhoneNumbers = {};
workEmails = {};
personalPhoneNumbers = {};
personalEmails = {};
choice = "";
index = 0;
length = 0;
}

void DisplayInfo() {

void AddInfo() {
cout<< endl;
string sc2;
cout<<("Enter their name: ");
getline(cin >> ws, sc2);
string name = sc2;
names.push_back(name);

cout<<("Enter their personal phone number: ");


getline(cin >> ws, sc2);
string personalPhone = sc2;
personalPhoneNumbers.push_back(personalPhone);

cout<<("Enter their personal email: ");


getline(cin >> ws, sc2);
string personalEmail = sc2;
personalEmails.push_back(personalEmail);

cout<<("Enter their work title: ");


getline(cin >> ws, sc2);
string title = sc2;
titles.push_back(title);

cout<<("Enter their work phone number: ");


getline(cin >> ws, sc2);
string workPhone = sc2;
workPhoneNumbers.push_back(workPhone);

cout<<("Enter their work email: ");


getline(cin >> ws, sc2);
string workEmail = sc2;
workEmails.push_back(workEmail);
length++;
view = "list";
}

int GetLength() {
return length;
}

void HandleChoice() {
if (choice==("q")) {
view = "quit";
}
else if (choice==("a") && view==("list")) {
view = "add";
}
else if (IsNumeric(choice) && view==("list")) {
int num = (stoi(choice) - 1);
if (num >= 0 && num < length) {
index = num;
view = "info";
}
}
}

void ShowList() {
cout<< endl;
string sc;
if (length == 0) {
cout<< ("(A)dd a new contact \n(Q)uit \n> ");
cin >> sc;
for (char c : sc) {
choice += (tolower(c));
}
}
else {
for (int i = 0; i < length; i++) {
cout<< (i + 1 ) << ") " << names.at(i)<<endl;
}
cout<< ("\n(#) Select a name \n(A)dd a new
contact\n(Q)uit \n> ");
cin >> sc;
for (char c : sc) {
choice += (tolower(c));
}
}
HandleChoice();
choice = "";
}

void Display() {
while (true) {
if (view==("list")) {
ShowList();
}
else if (view==("info")) {
DisplayInfo();
}
else if (view==("add")) {
cout<< endl;
AddInfo();
}
else if (view==("quit")) {
cout<< ("\nClosing the contact list...\n");
break;
}
}
}
private:
string view;
vector<string> names;
vector<string> titles;
vector<string> workPhoneNumbers;
vector<string> workEmails;
vector<string> personalPhoneNumbers;
vector<string> personalEmails;
string choice;
int index;
int length;

bool IsNumeric(string s) {
int value;

if (s == "") {
return false;
}

try {
value = stoi(s);
return true;
}
catch (runtime_error& e) {
return false;
}
}
};

//add class definitions above this line

int main(){

//add code below this line

Contacts contacts;
contacts.Display();

//add code above this line

return 0;

}
On the previous page, we already modified HandleChoice to deal with
numeric input. So let’s update the DisplayInfo function to display the
information. Print the elements from each vector using the attribute index.
After the information is displayed, change view back to "list" and then call
ShowList.

void DisplayInfo() {
cout<< endl;
cout<<names.at(index)<<endl;
cout<<"Personal email address: " << personalEmails.at(index)
<<endl;
cout<<"Personal phone number: " <<
personalPhoneNumbers.at(index)<<endl;
cout<<"Work title: " << titles.at(index)<<endl;
cout<<"Work email address: " << workEmails.at(index)<<endl;
cout<<"Work phone number: " << workPhoneNumbers.at(index)
<<endl;
view = "list";
ShowList();
}

Testing Your Code


This program should be complete now. To test it, add at least two contacts.
Select one of the contacts by entering the correlated number. Keep entering
a number to display the relevant contact information, a to add additional
contacts, or q to quit the program.

Code

Your code should look like this:

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

//add class definitions below this line

class Information {
public:
virtual void DisplayInfo() = 0;
virtual void AddInfo() = 0;
};

class Contacts : public Information {


public:
Contacts() {
view = "list";
names = {};
titles = {};
workPhoneNumbers = {};
workEmails = {};
personalPhoneNumbers = {};
personalEmails = {};
choice = "";
index = 0;
length = 0;
}

void DisplayInfo() {
cout<< endl;
cout<<names.at(index)<<endl;
cout<<"Personal email address: " <<
personalEmails.at(index)<<endl;
cout<<"Personal phone number: " <<
personalPhoneNumbers.at(index)<<endl;
cout<<"Work title: " << titles.at(index)<<endl;
cout<<"Work email address: " << workEmails.at(index)
<<endl;
cout<<"Work phone number: " << workPhoneNumbers.at(index)
<<endl;
view = "list";
ShowList();
}

void AddInfo() {
cout<< endl;
string sc2;
cout<<("Enter their name: ");
getline(cin >> ws, sc2);
string name = sc2;
names.push_back(name);

cout<<("Enter their personal phone number: ");


getline(cin >> ws, sc2);
string personalPhone = sc2;
personalPhoneNumbers.push_back(personalPhone);

cout<<("Enter their personal email: ");


getline(cin >> ws, sc2);
string personalEmail = sc2;
personalEmails.push_back(personalEmail);

cout<<("Enter their work title: ");


getline(cin >> ws, sc2);
string title = sc2;
titles.push_back(title);
cout<<("Enter their work phone number: ");
getline(cin >> ws, sc2);
string workPhone = sc2;
workPhoneNumbers.push_back(workPhone);

cout<<("Enter their work email: ");


getline(cin >> ws, sc2);
string workEmail = sc2;
workEmails.push_back(workEmail);
length++;
view = "list";
}

int GetLength() {
return length;
}

void HandleChoice() {
if (choice==("q")) {
view = "quit";
}
else if (choice==("a") && view==("list")) {
view = "add";
}
else if (IsNumeric(choice) && view==("list")) {
int num = (stoi(choice) - 1);
if (num >= 0 && num < length) {
index = num;
view = "info";
}
}
}

void ShowList() {
cout<< endl;
string sc;
if (length == 0) {
cout<< ("(A)dd a new contact \n(Q)uit \n> ");
cin >> sc;
for (char c : sc) {
choice += (tolower(c));
}
}
else {
for (int i = 0; i < length; i++) {
cout<< (i + 1 ) << ") " << names.at(i)<<endl;
}

cout<< ("\n(#) Select a name \n(A)dd a new


cout<< ("\n(#) Select a name \n(A)dd a new
contact\n(Q)uit \n> ");
cin >> sc;
for (char c : sc) {
choice += (tolower(c));
}
}
HandleChoice();
choice = "";
}

void Display() {
while (true) {
if (view==("list")) {
ShowList();
}
else if (view==("info")) {
DisplayInfo();
}
else if (view==("add")) {
cout<< endl;
AddInfo();
}
else if (view==("quit")) {
cout<< ("\nClosing the contact list...\n");
break;
}
}
}

private:
string view;
vector<string> names;
vector<string> titles;
vector<string> workPhoneNumbers;
vector<string> workEmails;
vector<string> personalPhoneNumbers;
vector<string> personalEmails;
string choice;
int index;
int length;

bool IsNumeric(string s) {
int value;

if (s == "") {
return false;
}

try {
value = stoi(s);
return true;
}
catch (runtime_error& e) {
return false;
}
}
};

//add class definitions above this line

int main(){

//add code below this line

Contacts contacts;
contacts.Display();

//add code above this line

return 0;

}
Lab Challenge

Problem

In the IDE to the left, the class Chef is already defined, as is the Display
function. However, it does not have a constructor. Create three
constructors that take one, two, and three parameters respectively. Note
that the attributes name and cuisine are set to "null" while michelinStars is
set to 0 by default.

Given Code

#include <iostream>
using namespace std;

//add class definitions below this line

class Chef {
public:

//add constructors below this line

//add constructors above this line

string GetName() {
return name;
}

string GetCuisine() {
return cuisine;
}

int GetStars() {
return michelinStars;
}

void Display() {
cout << GetName() << " is known for " << GetCuisine() << "
cuisine and has " << GetStars() << " Michelin stars." <<
endl;
}
private:
string name = "null";
string cuisine = "null";
int michelinStars = 0;
};

//add class definitions above this line

int main() {

//DO NOT EDIT code below this line

Chef c1("Marco Pierre White");


Chef c2("Rene Redzepi", "Nordic");
Chef c3("Thomas Keller", "French", 3);

c1.Display();
c2.Display();
c3.Display();

//DO NOT EDIT code above this line

return 0;

Expected Output

Three Chef objects are instantiated, each one using a different constructor.
Calling the Display function for each object should return the following
text:

Function Call Return Value


Marco Pierre White is known for
c1.Display() null cuisine and has 0 Michelin
stars.
Rene Redzepi is known for
c2.Display() Nordic cuisine and has 0
Michelin stars.
Thomas Keller is known for
c3.Display() French cuisine and has 3
Michelin stars.
Learning Objectives: Advanced
Topics

Include and create an object defined in a separate file

Understand what the scope resolution operator does

Manipulate class variables not attached to a class object

Create structs and enums

Compare objects
Using #include

Objects Defined in Other Files


Now that you’re familiar with creating, manipulating, and inheriting
objects, we’ll discuss how these concepts all come together when programs
are created. You may have noticed that a lot of the classes that you’ve
previously worked with is fairly lengthy. In fact, it’s not typical to include
class definitions within the same file as the main program. Usually, classes
are defined in other files but they can be accessed by the main program if
you set up the correct encapsulation.

“#include” in the Header


Ever noticed how #include <iostream> and using namespace std; are
usually present in the header of the file? The #include directs the system to
look for the file or library iostream. Then using namespace std; enables the
system to access functions from the class std without having to use the
scope resolution operator ::.

.guides/img/advanced/Include

Enter the following into main and Try It.

cout << "Hello world" << endl;


Next, comment the header line using namespace std; and run the program
again.

#include <iostream>
//using namespace std;

Without using namespace std;, the system has no clue which class to look
into to use cout. Now revise the code in main to look like this:

std::cout << "Hello world" << std::endl;

The scope resolution operator :: does the same job as using namespace
std;. Note that you will need to use std:: in front of every function you
decide to call.

Calling Functions From a Separate File


In the upper left of the text editor window, you will notice a tab that says
class.h. Click on this tab to see the content of the file.

.guides/img/advanced/HeaderFile

This is a header file which you can use to store your class definitions. There
is some syntax (at the beginning and the end) that you have to include in
order to specify that class.h is a header file, but ultimately you can reduce
the number of code in your main program by creating header files. All
header files should have #ifndef and #define (followed by their file name
and then an underscore _ and H) in their header and #endif towards the
end.
Now, go back into include.cpp, copy the entire code below and TRY IT.

//#include <iostream>
//using namespace std;
#include "class.h"

int main() {

//add code below this line

Greeting g("Hello world");


cout << g.GetGreeting() << endl;
g.SetGreeting("Hi world");
cout << g.GetGreeting() << endl;

//add code above this line

return 0;

Notice how we used #include "class.h" in the header of our file. This
enables a connection between the main file and the header file class.h
where the class definitions are found. Also note that because #include
<iostream> and using namespace std; are already included in class.h, you
can comment out or remove them in the main program.
Static Variables & Functions

Manipulating Non-Object Variables


Throughout this course, we’ve been creating class objects through the use
of a constructor, which is required in order for an object to access certain
class functions. However, if we want to manipulate a particular class
attribute without having to instantiate that particular object, you can use a
combination of the static keyword plus the scope resolution operator :: to
manipulate attributes. Copy the code below and TRY IT.
#include <iostream>
using namespace std;

//add class definitions below this line

class Player {
public:
Player() {
health = 100;
score = 0;
level = 1;
}
void PrintLevel() {
cout << level << endl;
}
static int ChangeLevel(int change) { //define static
function
level = change;
return level;
}

private:
int health;
int score;
static int level; //declare static variable
};

//add class definitions above this line

int Player::level = 0; //initalize static variable globally

int main() {

//add code below this line

Player mario;
mario.PrintLevel(); //calling class function, object required
cout << Player::ChangeLevel(5) << endl; //calling static
function, object not needed

//add code above this line

return 0;

Notice how when calling the ChangeLevel function, we needed the class
name Player followed by the scope resolution operator :: followed by the
function name ChangeLevel and any parameter arguments. Calling a static
function enabled us to change the attribute level to 5 without calling the
function on the object itself.

When defining and calling static functions, keep the following in mind:
* Within a class, static functions can only access other static members; for
example, if level was not a static variable in the example above,
ChangeLevel will not be able to access it.
* A static variable should be defined or initialized globally outside of any
class or function. In other words, a static variable should not be initialized
within a class or within the main function.
* Use the scope resolution operator :: as defined above to access static
functions that modify static variables.

Static Variables
It’s important to note that static variables are variables that are created
only once. They cannot be created again for the duration of the program.
The following code will showcase how the static variable level works
throughout the program.
#include <iostream>
using namespace std;

//add class definitions below this line

class Player {
public:
Player() {
health = 100;
score = 0;
}
void PrintLevel() {
cout << level << endl;
}
static int ChangeLevel(int change) {
level = change;
return level;
}

private:
int health;
int score;
static int level;
};

//add class definitions above this line

int Player::level = 5; //initialize static variable level to 5

int main() {

//add code below this line

Player mario;
mario.PrintLevel();
cout << Player::ChangeLevel(6) << endl;
Player luigi;
luigi.PrintLevel();

//add code above this line

return 0;

}
Again, note that static variables are initialized outside of a class globally.
This is to prevent the variable from being duplicated. By definition, static
variables are only created once. This is why when the Player object luigi is
created, its level attribute is the same as mario’s even though its level was
never changed. Essentially, both objects share the same static variable
level. Changes made to level will be reflected in all objects that have that
attribute.
Structs & Enums

What are Structs?


Structs are like classes, except everything inside a struct is public.
Therefore, anything external to the struct can alter its members. For
example:

//add class definitions below this line

struct Person {
string name;
int age;
double salary;
};

//add class definitions above this line

int main() {

//add code below this line

Person p;
p.age = 50;
cout << p.age << endl;

//add code above this line

return 0;

In the example above, after creating the struct called Person, we are able to
access the age attribute by simply using dot notation and specifying the
attribute. If security is not an issue, structs are useful at putting together a
collection of attributes that are highly modifiable and accessible.

Setting Values with Enums


Enums are similar to switch-case statements where particular values are
assigned to particular cases. See example below:
//add class definitions below this line

enum grades {A = 90, B = 80, C = 70, D = 60};

//add class definitions above this line

int main() {

//add code below this line

grades grade;

grade = A;
cout << "Grade = " << grade << endl;

//add code above this line

return 0;

Cases (variables) of the alphabet from A through D have been assigned to


variable integers from 90 backwards to 60 in increments of 10. When you
create a grades enum, you can assign it to any of the cases specified in the
enum definition. This will signal to the system to assign the grades enum to
appropriate integer equivalent.

NOTE: Enum values must be integers. For example, you can’t create an
enum with string values.

challenge

Try this variation:


Change enum grades {A = 90, B = 80, C = 70, D = 60}; in the
code above to enum grades {A, B, C, D};.
Change grade = A; to grade = B;.
Change enum grades {A, B, C, D}; to enum grades {A = 90, B, C,
D};.
Change enum grades {A = 90, B, C, D}; to enum grades {A, B, C
= 5, D};.
Change grade = B; to grade = D;.
NOTE: If the enum variables are not assigned values, the first variable will
be 0 by default. The second variable will be 1 and so on. Additionally, the
incrementation of 1 happens following the first value of the initialized
enum variable. For example, enum fruit {apple = 5, banana, orange};
causes apple to have a value of 5 and each variable after will have a value
incremented by 1. This means banana will have a value of 6 and orange will
have a value of 7. On the other hand, enum fruit {apple, banana = 5,
orange}; causes apple to be 0, banana to be 5, and orange to be 6.
Object Equality

Comparing Object Types


You can compare object types by implementing the typeid function. For
example, the code below showcases the comparison between two Player
objects called mario and luigi using typeid.

//add class definitions below this line

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

private:
int health;
int score;
int level;
};

//add class definitions above this line

int main() {

//add code below this line

Player mario;
Player luigi;
cout << boolalpha;
cout << (typeid(mario) == typeid(luigi)) << endl;

//add code above this line

return 0;

}
Since both mario and luigi are of the class Player, their typeid will be the
same. This is why cout << (typeid(mario) == typeid(luigi)) << endl;
returns true. Unfortunately, typeid does not check to ensure that both
objects contain the same exact attribute values. However, you can create a
user-defined class to check for that.

Comparing Same-Class Objects


Let’s create a static member function called ComparePlayers. This function
takes in two Player objects as parameters, then checks to see if each of
their attributes is equal to the other. If their attributes are equal, true is
returned. Else, false is returned. Another member function called
NextLevel is also created.

//add class definitions below this line

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

static bool ComparePlayers(Player p1, Player p2) {


if ((p1.health == p2.health) &&
(p1.score == p2.score) &&
(p1.level == p2.level)) {
return true;
}
else {
return false;
}
}

void NextLevel() {
level++;
}

private:
int health;
int score;
int level;
};

//add class definitions above this line


int main() {

//add code below this line

Player mario;
Player luigi;
cout << boolalpha;
cout << Player::ComparePlayers(mario, luigi) << endl;

//add code above this line

return 0;

challenge

Try this variation:


Replace the code in main with:

//add code below this line

Player mario;
Player luigi;
cout << boolalpha;
mario.NextLevel();
cout << Player::ComparePlayers(mario, luigi) << endl;

//add code above this line

Notice how when mario’s level changed, the ComparePlayers function


returns false when mario and luigi are compared.
Lab 1

Lab 1
In this module, we’ve learned how to create separate header files so that
we can call them without having to place them into the main file. This will
dramatically reduce the length of code needed in the main file. Make sure
your main file (lab1.cpp) and your header file (class.h) contain the code
below.

main

#include "class.h"

int main() {

//add code below this line

Greeting g("Hello world");


cout << g.GetGreeting() << endl;
g.SetGreeting("Hi world");
cout << g.GetGreeting() << endl;

//add code above this line

return 0;

header
#ifndef CLASS_H
#define CLASS_H
#include <iostream>
using namespace std;

//add class definitions below this line

class Greeting {
public:
Greeting(string g) {
greeting = g;
}

string GetGreeting() {
return greeting;
}

void SetGreeting(string new_greeting) {


greeting = new_greeting;
}

void PrintGreeting(){
cout << GetGreeting() << endl;
}

private:
string greeting;
};

//add class definitions above this line

#endif

Now, try to add to the existing class.h by including a class called Farewell.
This class should include all of the equivalent functions of Greeting.

For example, if:

Farewell f("Goodbye world");


cout << f.GetFarewell() << endl;
f.SetFarewell("Bye world");
cout << f.GetFarewell() << endl;

Then the result should be:


Goodbye world
Bye world
Lab 2

Lab 2
You are provided the following header and main files:

point.h

#ifndef SLOPE_H
#define SLOPE_H

//add definitions below this line

//add definitions above this line

#endif

slope.h

#ifndef SLOPE_H
#define SLOPE_H

//add definitions below this line

//add definitions above this line

#endif

lab2.cpp
#include <iostream>
using namespace std;
#include "point.h"
#include "slope.h"

int main() {

//add code below this line

//add code above this line

return 0;

In this lab, you’ll be working with these three files. The idea is to create a
struct called point within point.h, then create a static function called
CalculateSlope within slope.h, and finally run a few commands within
lab2.cpp to print some results.

point.h
In this header file, we will create a struct called point which contains just
two attributes, int x and int y. Remember, structs are public by default
which means they are easily accessible.

//add definitions below this line

struct point {
int x;
int y;
};

//add definitions above this line

slope.h
In this header file, we will create a class called Slope. This class only has
one static member function called CalculateSlope. CalculateSlope takes in
two point structures and returns the calculated slope between them.
//add definitions below this line

class Slope {
public:
static double CalculateSlope(point a, point b) {
return ( (double) (b.y - a.y) / (double) (b.x - a.x) );
}
};

//add definitions above this line

lab2.cpp
Now it’s time to test our header files within main. We are going to create
two point structures, assign values to their attributes, then call
CalculateSlope on the two points. Note that we do not need to create a
Slope object before calling CalculateSlope since it is a static function.
Simply use the scope resolution operator :: to access the function as shown
in the code below.

//add code below this line

point a;
point b;
a.x = 0;
a.y = 0;
b.x = 2;
b.y = 2;
cout << Slope::CalculateSlope(a, b) << endl;

//add code above this line

challenge

Try these variations:


Replace the code in main with:
//add code below this line

point a;
point b;
a.x = 1;
a.y = 2;
b.x = 10;
b.y = 20;
cout << Slope::CalculateSlope(a, b) << endl;

//add code above this line

Change the entire lab2.cpp to look like this:


#include <iostream>
using namespace std;

struct point {
int x;
int y;
};

class Slope {
public:
static double CalculateSlope(point a, point b) {
return ( (double) (b.y - a.y) / (double) (b.x - a.x)
);
}
};

int main() {

//add code below this line

point a;
point b;
a.x = 1;
a.y = 2;
b.x = 10;
b.y = 20;
cout << Slope::CalculateSlope(a, b) << endl;

//add code above this line

return 0;

Note that including the header files produces the same result as including
the struct point and class Slope within main. However, the header files
enable the main program to be less cluttered with definitions.
Lab Challenge

Problem

Create a BankAccount struct in the IDE to the left which has two double
attributes checking and savings. Create a function called ToString within
the struct that prints a representation of a BankAccount struct which
includes these attributes.

You MUST use struct within your code in order to receive credit for this
challenge.

Given Code

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

//add definitions below this line

//add definitions above this line

int main() {

//DO NOT EDIT code below this line

BankAccount account1;
account1.checking = 2432;
account1.savings = 89.52;
BankAccount account2;
account2.checking = 1998;
account2.savings = 239.43;
account1.ToString();
account2.ToString();

//DO NOT EDIT code above this line

return 0;

}
Setting decimal places

You can use the code cout << setprecision(2) << fixed followed by a
specified value to set that value to two decimal places.

Testing Your Code


The code in main is used to test your definitions. DO NOT EDIT this code!

//DO NOT EDIT code below this line

BankAccount account1;
account1.checking = 2432;
account1.savings = 89.52;
BankAccount account2;
account2.checking = 1998;
account2.savings = 239.43;
account1.ToString();
account2.ToString();

//DO NOT EDIT code above this line

Expected Result

BankAccount[checking=2432.00, savings=89.52]
BankAccount[checking=1998.00, savings=239.43]

You might also like