Part 4
Part 4
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;
};
ExampleClass my_example;
my_example.SetN(5, 7);
my_example.Describe();
cout << my_example.Sum() << endl;
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.
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.
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;
};
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;
challenge
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.
class Phone {
public:
Phone(string mo, int st, int me) {
model = mo;
storage = st;
megapixels = me;
}
string model;
int storage;
int megapixels;
};
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.
class Phone {
public:
Phone(string mo, int st, int me) {
model = mo;
storage = st;
megapixels = me;
}
private:
string model;
int storage;
int megapixels;
};
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;
};
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.
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;
}
};
PrivateExample my_example(5);
my_example.PrivateFunction();
PrivateExample my_example(5);
my_example.PublicFunction();
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;
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;
string s = "house";
Words vowels(s, s.length());
vowels.Printer();
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.
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;
};
challenge
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
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.
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 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.
Possible Solution
//class definitions
public:
void SetStorage(int new_storage) {
storage = new_storage;
}
//main function
my_phone.SetStorage(128);
cout << my_phone.GetStorage() << endl;
my_phone.SetMegapixels(6);
cout << my_phone.GetMegapixels() << endl;
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.
class Person {
public :
Person(string n, int a) {
name = n;
age = a;
}
string GetName() {
return name;
}
int GetAge() {
return age;
}
private:
string name;
int age;
};
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.
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.
Possible Solution
Add data validation to the SetName function so that all strings with
one or more characters are valid.
Possible Solution
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.
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;
};
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:
Full Code
#include <iostream>
#include <vector>
using namespace std;
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;
int main() {
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;
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;
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;
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;
int main() {
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.
Updated Code
#include <iostream>
#include <vector>
using namespace std;
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 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;
int main() {
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;
int main() {
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
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
};
Now declare an instance of the Superhero class and print the value of the
name and age attributes using their getter functions.
Superhero s;
cout << s.GetName() << endl;
cout << s.GetAge() << endl;
Solution
Superhero s;
s.SetName("Peter Parker");
s.SetOccupation("Student");
cout << s.GetOccupation() << endl;
s.SayHello();
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.
Source: https://www.programiz.com/cpp-programming/public-protected-
private-inheritance
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
.guides/img/inheritance/AccessModifierTable
Source: https://www.tutorialspoint.com/cplusplus/cpp_inheritance.htm
private:
string s_derived;
};
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.
string s_main;
Derived dc;
dc.ReturnPublic(s_main);
The reason why main is able to call ReturnPublic is due to the fact that
ReturnPublic is a public member function within Derived.
challenge
private:
string s_derived;
};
void ReturnProtected(string s) {
Protected(s_derived); //protected function inherited from
Base
}
private:
string s_derived;
};
string s_main;
Derived dc;
dc.ReturnProtected(s_main);
dc.Public(s_main);
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
void ReturnProtected(string s) {
Protected(s_derived);
}
private:
string s_derived;
};
string s_main;
Derived dc;
dc.ReturnProtected(s_main);
dc.Public(s_main);
void ReturnProtected(string s) {
Protected(s_derived);
}
/*protected:
void Public(string s) { (inherited as protected from Base)
s = "public";
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;
};
//protected:
//int y;
};
class Base {
public:
int x;
protected:
int y;
private:
int z;
};
protected:
int y;
private:
int z;
};
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
Sample Code:
Constructor2(Param p1, Param p2) : Constructor1(p1, p2) {}
Here is an example:
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.
Superhero s;
cout << "s is of type: " << typeid(s).name() << endl;
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;
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.
class Person {
public:
void Greeting() {
cout << "I'm a Person" << endl;
}
};
class Animal {
public:
void Greeting() {
cout << "I'm an Animal" << endl;
}
};
void Substitution(Person p) {
p.Greeting();
}
Superhero s;
Substitution(s);
challenge
Animal a;
Substitution(a);
void Substitution(Superhero s) {
s.Greeting();
}
Person p;
Substitution(p);
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.
class Person {
public:
void Greeting() {
cout << "I'm a Person" << endl;
}
};
See what happens when you you revise the Substitution function and main
to look like below:
void Substitution(Person p) {
p.Greeting();
}
Superhero s;
Substitution(s);
Hero h;
Substitution(h);
.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
string GetSecretIdentity() {
return secret_identity;
}
private:
string secret_identity;
};
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.
string GetSecretIdentity() {
return secret_identity;
}
string GetNemesis() {
return nemesis;
}
private:
string secret_identity;
string nemesis;
};
int main() {
//add code below this line
return 0;
string GetSecretIdentity() {
return secret_identity;
}
string GetNemesis() {
return nemesis;
}
void RevealSecretIdentity() {
cout << "My real name is " << secret_identity << '.' <<
endl;
}
private:
string secret_identity;
string nemesis;
};
string GetSecretIdentity() {
return secret_identity;
}
string GetNemesis() {
return 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;
};
int main() {
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.
void SayHello() {
cout << "My name is " << GetName() << ", and criminals
fear me." << endl;
}
void SayAge() {
cout << "Age is just a number." << endl;
}
void SayHello() {
cout << "My name is " << GetName() << ", and criminals
fear me." << endl;
}
void SayAge() {
cout << "Age is just a number." << endl;
}
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
void SayHello() {
cout << "Hello, my name is " << name << '.' << endl;
}
void SayAge() {
cout << "I am " << age << " years old." << endl;
}
to
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.
challenge
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.
ClassC c;
c.Bonjour();
c.ClassB::Hello();
c.ClassA::Hello();
void AuRevoir() {
cout << "Au revoir" << endl;
}
};
int main() {
ClassC c;
c.AuRevoir();
return 0;
ClassC c;
c.Hello();
c.ClassB::Hello();
c.ClassA::Hello();
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
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;
};
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();
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;
};
int GetWidth() {
return width;
}
void DrawBox() {
for (int i = 0; i < width; i++) {
DrawLine();
}
}
private:
int width;
};
challenge
Given Code
#include <iostream>
using namespace std;
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;
};
void DrawBox() {
for (int i = 0; i < width; i++) {
DrawLine();
}
}
private:
int width;
};
int main() {
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.
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;
};
int GetWidth() {
return width;
}
void DrawBox() {
for (int i = 0; i < width; i++) {
DrawLine();
}
}
private:
int width;
};
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;
}
}
}
};
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!
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:
Given Code
#include <iostream>
using namespace std;
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;
}
int GetLength() {
return length;
}
string GetGenre() {
return genre;
}
string GetAlbum() {
return album;
}
string GetArtist() {
return artist;
}
private:
string title;
int length;
string genre;
string album;
string artist;
};
int main() {
return 0;
Expected Output
Overload a function
Override a function
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.
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;
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
int a = 5;
string b = "true";
cout << (a + b) << endl;
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;
}
};
Alpha test_object;
test_object.Show();
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
Solution
class Alpha {
public:
void Show() {
cout << "I am from class Alpha" << endl;
}
void Hello() {
cout << "Hello from Alpha" << endl;
}
};
void Hello() {
cout << "Hello from Bravo" << endl;
}
};
int main() {
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.
class TestClass {
public:
int Sum(int n1, int n2, int n3) {
return n1 + n2 + n3;
}
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.
TestClass tc;
cout << tc.Sum(1, 2, 3) << endl;
cout << tc.Sum(1, 2) << endl;
challenge
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 main() {
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;
return 0;
class Person {
public:
Person() {}
string Info() {
return (name + " lives at " + to_string(number) + ' ' +
street + '.');
}
private:
string name;
int number;
string street;
};
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.
Person p1;
Person p2("Calvin", 37, "Main Street");
cout << p1.Info() << endl;
cout << p2.Info() << endl;
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
class Person {
public:
//Person() {}
string Info() {
return (name + " lives at " + to_string(number) + " "
+ street + ".");
}
private:
string name;
int number;
string street;
};
Person p1;
//Person p2("Calvin", 37, "Main Street");
cout << p1.Info() << endl;
//cout << p2.Info() << endl;
class Person {
public:
//Person() {}
string Info() {
return (name + " lives at " + to_string(number) + " "
+ street + ".");
}
private:
string name;
int number;
string street;
};
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.
class Shape {
public:
virtual double Area() = 0;
};
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.
class Shape {
public:
virtual double Area() = 0;
double GetBase() {
return base;
}
double GetHeight() {
return height;
}
protected:
double base;
double height;
};
double Area() {
return base * height / 2;
}
};
double Area() {
return base * height;
}
};
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.
As expected, the code returns the correct calculations for the Triangle and
Rectangle objects.
challenge
Shape s;
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.
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.
class Information {
public:
virtual void DisplayInfo() = 0;
virtual void AddInfo() = 0;
};
void AddInfo() {
}
};
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.
class Information {
public:
virtual void DisplayInfo() = 0;
virtual void AddInfo() = 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;
};
void DisplayInfo() {
void AddInfo() {
void ShowList() {
Contacts contacts;
contacts.Display();
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
#include <iostream>
#include <vector>
using namespace std;
class Information {
public:
virtual void DisplayInfo() = 0;
virtual void AddInfo() = 0;
};
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();
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.
#include <iostream>
#include <vector>
using namespace std;
class Information {
public:
virtual void DisplayInfo() = 0;
virtual void AddInfo() = 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(){
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();
}
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);
Contacts contacts;
contacts.Display();
cout<< contacts.GetLength()<< endl;
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
Code
#include <iostream>
#include <vector>
using namespace std;
class Information {
public:
virtual void DisplayInfo() = 0;
virtual void AddInfo() = 0;
};
void DisplayInfo() {
void AddInfo() {
cout << endl;
string sc2;
cout << ("Enter their name: ");
getline(cin >> ws, sc2);
string name = sc2;
names.push_back(name);
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;
};
int main(){
Contacts contacts;
contacts.Display();
cout << contacts.GetLength() << endl;
return 0;
}
Lab 3
#include <iostream>
#include <vector>
using namespace std;
class Information {
public:
virtual void DisplayInfo() = 0;
virtual void AddInfo() = 0;
};
void DisplayInfo() {
void AddInfo() {
cout << endl;
string sc2;
cout << ("Enter their name: ");
getline(cin >> ws, sc2);
string name = sc2;
names.push_back(name);
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;
};
Contacts contacts;
contacts.Display();
cout << contacts.GetLength() << endl;
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:
Contacts contacts;
contacts.Display();
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 = "";
}
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;
}
}
.guides/img/Polymorphism/lab3_pic1
Thomas Hobbes
555 666-7777
t_hobbes@email.net
Philosopher
555 888-9999
tom_hobbes@work.org
.guides/img/Polymorphism/lab3_pic2
Code
#include <iostream>
#include <vector>
using namespace std;
//add class definitions below this line
class Information {
public:
virtual void DisplayInfo() = 0;
virtual void AddInfo() = 0;
};
void DisplayInfo() {
void AddInfo() {
cout << endl;
string sc2;
cout << ("Enter their name: ");
getline(cin >> ws, sc2);
string name = sc2;
names.push_back(name);
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;
}
}
};
int main(){
Contacts contacts;
contacts.Display();
return 0;
}
Lab 4
#include <iostream>
#include <vector>
using namespace std;
class Information {
public:
virtual void DisplayInfo() = 0;
virtual void AddInfo() = 0;
};
void DisplayInfo() {
void AddInfo() {
cout<< endl;
string sc2;
cout<<("Enter their name: ");
getline(cin >> ws, sc2);
string name = sc2;
names.push_back(name);
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;
}
}
};
int main(){
Contacts contacts;
contacts.Display();
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();
}
Code
#include <iostream>
#include <vector>
using namespace std;
class Information {
public:
virtual void DisplayInfo() = 0;
virtual void AddInfo() = 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);
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;
}
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;
}
}
};
int main(){
Contacts contacts;
contacts.Display();
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;
class Chef {
public:
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;
};
int main() {
c1.Display();
c2.Display();
c3.Display();
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:
Compare objects
Using #include
.guides/img/advanced/Include
#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:
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.
.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() {
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
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
};
int main() {
Player mario;
mario.PrintLevel(); //calling class function, object required
cout << Player::ChangeLevel(5) << endl; //calling static
function, object not needed
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;
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;
};
int main() {
Player mario;
mario.PrintLevel();
cout << Player::ChangeLevel(6) << endl;
Player luigi;
luigi.PrintLevel();
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
struct Person {
string name;
int age;
double salary;
};
int main() {
Person p;
p.age = 50;
cout << p.age << endl;
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.
int main() {
grades grade;
grade = A;
cout << "Grade = " << grade << endl;
return 0;
NOTE: Enum values must be integers. For example, you can’t create an
enum with string values.
challenge
class Player {
public:
Player() {
health = 100;
score = 0;
level = 1;
}
private:
int health;
int score;
int level;
};
int main() {
Player mario;
Player luigi;
cout << boolalpha;
cout << (typeid(mario) == typeid(luigi)) << endl;
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.
class Player {
public:
Player() {
health = 100;
score = 0;
level = 1;
}
void NextLevel() {
level++;
}
private:
int health;
int score;
int level;
};
Player mario;
Player luigi;
cout << boolalpha;
cout << Player::ComparePlayers(mario, luigi) << endl;
return 0;
challenge
Player mario;
Player luigi;
cout << boolalpha;
mario.NextLevel();
cout << Player::ComparePlayers(mario, luigi) << endl;
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() {
return 0;
header
#ifndef CLASS_H
#define CLASS_H
#include <iostream>
using namespace std;
class Greeting {
public:
Greeting(string g) {
greeting = g;
}
string GetGreeting() {
return greeting;
}
void PrintGreeting(){
cout << GetGreeting() << endl;
}
private:
string greeting;
};
#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.
Lab 2
You are provided the following header and main files:
point.h
#ifndef SLOPE_H
#define SLOPE_H
#endif
slope.h
#ifndef SLOPE_H
#define SLOPE_H
#endif
lab2.cpp
#include <iostream>
using namespace std;
#include "point.h"
#include "slope.h"
int main() {
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.
struct point {
int x;
int y;
};
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) );
}
};
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.
point a;
point b;
a.x = 0;
a.y = 0;
b.x = 2;
b.y = 2;
cout << Slope::CalculateSlope(a, b) << endl;
challenge
point a;
point b;
a.x = 1;
a.y = 2;
b.x = 10;
b.y = 20;
cout << Slope::CalculateSlope(a, b) << endl;
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() {
point a;
point b;
a.x = 1;
a.y = 2;
b.x = 10;
b.y = 20;
cout << Slope::CalculateSlope(a, b) << endl;
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;
int main() {
BankAccount account1;
account1.checking = 2432;
account1.savings = 89.52;
BankAccount account2;
account2.checking = 1998;
account2.savings = 239.43;
account1.ToString();
account2.ToString();
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.
BankAccount account1;
account1.checking = 2432;
account1.savings = 89.52;
BankAccount account2;
account2.checking = 1998;
account2.savings = 239.43;
account1.ToString();
account2.ToString();
Expected Result
BankAccount[checking=2432.00, savings=89.52]
BankAccount[checking=1998.00, savings=239.43]