Lecture 5/4: C++ Classes
May 4, 2020
đź“‚Associated files
C++ Classes
CS 106B: Programming Abstractions
Spring 2020, Stanford University Computer Science Department
Lecturers: Chris Gregg and Julie Zelenski
Slide 2
Announcements
- Virtual Black LaIR is an awesome resource put together by Stanford Black in CS. Check out the poster below for information on how to join!
- Pixar Night is happening on Thurday night from 4:30-6pm! Zoom info will be posted on the event page later this week.
- Assignment 4 YEAH session is tonight from 5-6pm. Zoom info on the Zoom details page. Materials will be posted online shortly after the session ends.
- Mid-quarter assessments will be happening on Wednesday-Sunday of next week (May 13-May 17)! Your section leaders will be reaching out to you at the end of this week to schedule times with you. Remember that you will be signing up for an hour long block – 30 minutes of prep and 30 minutes of time with your section leader.
- We will be posting more resources (recorded practice session, practice problems, etc.) later this week!
- Assignment 4 is out and will be due on Friday, May 8. For this assignment only, all students wll be asked to do the warmup and at least 2 of the 3 remaining parts. However, if you have the time to do all 3, we highly recommend doing them all!
Slide 3
Today's Goals:
- Introduce C++ Classes
- Discuss Object Oriented Programming (OOP)
- Talk about encapsulation as it relates to classes and objects
Slide 4
Bouncing Balls Demo
Slide 5
Introduction to C++ Classes
- We have talked about structs already (briefly), and we'll start with them now. Structs give us the ability to package data into one place. The types of the data do not have to be the same, so we could store strings, ints, bools, etc. We could say that structs are the "Lunchable" of the C++ world – they package different elements together so we can use them together:
struct Lunchable { string meat; string dessert; int numCrackers; bool hasCheese; };
- But why stop at data? If we're packaging stuff up, let's also package up the functions. It would be really nice if we could do this:
struct Lunchable { string meat; string dessert; int numCrackers; bool hasCheese; int countCalories(); // what? A function? };
- Guess what? We **can** do this!
- Once we have the ability to package up data and functions into one structure, we have a super-powerful tool, called an object that knows how to perform functions on itself, and carries around its own data.
- So-called Object Oriented Programming has led to the creation of most of the large programs we use today.
Slide 6
The need for new types
- A C++ "class" is simply a very-slightly modified struct (details to follow).
- As with structs, we sometimes want new types:
- A calendar program might want to store information about dates, but C++ does not have a Date type. 

- A student registration system needs to store info about students, but C++ has no Student type. 

- A music synthesizer app might want to store information about users' accounts, but C++ has no Instrument type.
Slide 7
Classes: Encapsulation
- The only difference between a
struct
and a class is the notion of defaults regarding encapsulation.- A struct defaults to public members, which can be accessed outside of the class itself, and a class defaults to private members, which can only be accessed inside the class functions.
-
"Encapsulation" allows the designer of a class to build a "wall of abstraction" around their data:
- Here is some example code for the image above:
int main() { BankAccount checking("Bob", 42); checking.withdraw(80); cout << checking.getBalance() << endl; }
- Bob only has $42 in his account, and therefore when he tries to withdraw $80, we get an error.
main
cannot directly change the amount of money in Bob's account – in other words,main
does not have access to the internal variable that is storing the amount – it has to use theBankAccount
constructor, and has to use thewithdraw
function from theBankAccount
class. IfBankAccount
allowed direct access, it would not be able to provide an error (i.e., it would have no idea that the variable had been changed). - The
checking.getBalance()
function is also encapsulated – the only way formain
to get Bob's balance is to call the function, and it cannot access the underlying data directly.
Slide 8
Classes: Encapsulation
- The reason we want encapsulation is so that the end user of our class does not have direct access to the data – we, as the class designer, control the data completely:
- Suppose we did allow direct access to the bank data:
int main() { BankAccount checking("Bob", 42); checking.balance = 81.2345; }
- If we allowed this, the
BankAccount
class might not be in a state that it can handle – it might have too many decimal places for a monetary value, for example. This is what encapsulation is all about: the class itself controls the data. - So, we block the ability for someone using our class to directly touch the data, and we force them to go through our own functions:
int main() { BankAccount checking("Bob", 42); checking.setBalance(81.2345); }
- Because we control the function, we can do a check on this, and enforce the "only two decimals" limit (for example).
Slide 9
Elements of a Class
- member variables: State inside each object.
- Also called "instance variables" or "fields"
- Declared as private
- Each object created has a copy of each field.
- member functions: Behavior that executes inside each object.
- Also called "methods"
- Each object created has a copy of each method.
- The method can interact with the data inside that object.
- constructor: Initializes new objects as they are created.
- Sets the initial state of each new object.
- Often accepts parameters for the initial state of the fields.
Slide 10
The Class Interface Divide
- When building a class, we provide an interface to the user of the class that details how the class works. The user often does not have access to the code itself, but the interface (and any other documentation) usually suffices to use the class properly.
- The interface is generally put into a header file, e.g.,
name.h
- The client of the class reads the header file to get the declarations so it can compile its own code to use the class
- The header file shows the class functions and variables (even though some may be private – see this post about why)
- The interface is generally put into a header file, e.g.,
- The source code for the class is held in a
.cpp
file, e.g.,name.cpp
.- The
.cpp
file is written by the implementer of the class, and they implement all of the class functions. - This is often delivered to the user in compiled form, or in a library. The client generally does not have or need access to this code (though you can get it for open source code libraries)
- The
Slide 11
Structure of a header file
// classname.h
#pragma once
class ClassName {
// class definition
};
-
The
pragma once
declaration basically says, "if you see this file more than once while compiling, ignore it after the first time" (so the compiler doesn't think you're trying to define things more than once) - In more detail:
// in ClassName.h class ClassName { public: ClassName(parameters); // constructor returnType func1(parameters); // member functions returnType func2(parameters); // (behavior inside returnType func3(parameters); // each object) private: type var1; // member variables type var2; // (data inside each object) type func4(); // (private function) };
- Any class instance can directly use anything defined as
public
, but you never directly call a constructor. To create an instance of a class, you declare it like this in simple cases (just like declaring a variable):MyClass a;
- A client can call all public functions, e.g.,
a.func1(argument)
- A class instance cannot directly use anything defined as private:
MyClass a; // declare an instance called "a" a.var1 = 2; // ERROR! "var1" is a private variable a.func4(); // ERROR! "func4" is a private function
Slide 12
Constructors and (eventually) Destructors
// in MyClass.h
class MyClass {
public:
MyClass(); // default constructor
MyClass(parameters); // constructor
...
};
- When a class instance is created, we say that it is constructed:
string s1; // uses default constructor string s2("I'm a string"); // uses a constructor // that takes 1 string parameter string s3 = "I'm a string"; // different! (we'll get to that)
Slide 13
The Implicit Parameter
- The implicit parameter for a class is the object on which the member function is called.
- During the call
chris.withdraw(...)
, the object namedchris
is the implicit parameter. 
 - During the call
julie.withdraw(...)
, the object namedjulie
is the implicit parameter. 
 - The member function can refer to that object's member variables.
- We say that it executes in the context of a particular object.
- The function can refer to the data of the object it was called on.
- It behaves as if each object has its own copy of the member functions.
- During the call
Slide 14
The this
pointer
- In C++ has a
this
pointer to refer to the current object (this is similar toself
in Python)- Syntax:
this->member
- Common usage: In the constructor, so parameter names can match the names of the object's member variables:
BankAccount::BankAccount(string name, double balance) { this->name = name; this->balance = balance; }
this
uses->
not.
because it is a pointer. We'll discuss pointers soon!
- Syntax:
Slide 15
Let's work on an example: the Fraction
class
- As an example of a class, we're going to define a
Fraction
class that can deal with rational numbers directly, without decimals. - We are going to walk through the class one step at a time, demonstrating the various parts of a class as we go.
Slide 16
Questions we must answer about the Fraction
class
- What data should the class hold?
- What kinds of functions (public / private) should our class have?
- What constructors could we have?
- What is a good value for a default fraction?
Slide 17
Fraction Class Outline
class Fraction {
public:
// Things we want the class clients to see go here
private:
// Things we want to encapsulate from the clients go here
};
- What data should a
Fraction
class have?- A
numerator
and adenominator
:class Fraction { public: // Things we want the class clients to see go here private: int num; // the numerator int denom; // the denominator };
- Why are
num
anddenom
private?- We want to encapsulate them
- A
- What functions should a
Fraction
class have?- How about these public functions?
class Fraction { public: void add(const Fraction &f); void multiply(const Fraction &f); double decimal(); int getNum(); int getDenom(); friend ostream& operator<< (ostream& out, const Fraction &frac); private: int num; // the numerator int denom; // the denominator };
- How about these public functions?
- Why are they public?
- The client can call them directly
- What is this
friend
business? And what isoperator<<
, etc.?- This defines an operator overload to make it possible to use the
<<
operator withcout
for our class. We will write the function soon.
- This defines an operator overload to make it possible to use the
Slide 18
The Fraction
class constructors
- We also need two other functions to construct the class:
class Fraction { public: Fraction(); // the "default" constructor Fraction(int num, int denom); // a second constructor void add(const Fraction &f); void multiply(const Fraction &f); double decimal(); int getNum(); int getDenom(); friend ostream& operator<< (ostream& out, const Fraction &frac); private: int num; // the numerator int denom; // the denominator };
- We have to answer what a default fraction should look like
1/1
probably makes sense (but0/0
does not!)- We have a constructor to let the client create a fraction of their own choosing, e.g.,
3/4
Slide 19
Private functions
- We might want a couple of other functions that will get used behind the scenes. The client does not need them, so they are private.
- A
reduce()
function is useful to keep reduced versions of the fractions -
The
reduce()
function needs agcd()
(greatest common divisor) to do its magic. ``` class Fraction { public: Fraction(); // the "default" constructor Fraction(int num, int denom); // a second constructorvoid add(const Fraction &f); void multiply(const Fraction &f); double decimal(); int getNum(); int getDenom(); friend ostream& operator« (ostream& out, const Fraction &frac);
private: int num; // the numerator int denom; // the denominator void reduce(); int gcd(int u, int v); }; ```
- A
Slide 20
Last but not least…
- We have defined most of our
fraction.h
file, but we do need a couple of things at the top:#pragma once #include<ostream> // for our operator<< overload using namespace std; class Fraction { public: Fraction(); // the "default" constructor Fraction(int num, int denom); // a second constructor void add(const Fraction &f); void multiply(const Fraction &f); double decimal(); int getNum(); int getDenom(); friend ostream& operator<< (ostream& out, const Fraction &frac); private: int num; // the numerator int denom; // the denominator void reduce(); int gcd(int u, int v); };
Slide 21
The Fraction
Class – implementation
- Let's start writing our functions. We do this in our fraction.cpp file, and we have to define the class that each function belongs to. We also cannot forget to include our header file!
#include "fraction.h"
-
The default constructor is used when someone wants to just create a default fraction:
Fraction frac;
- So, here is the default constructor code:
Fraction::Fraction() { num = 1; denom = 1; }
- This is pretty simple! We are just setting our two class variables to default values.
- What is the strange function declaration?
- We need to tell the compiler what class the function belongs to, and we do that with the class name (
Fraction
) and the _scope resolutoin operator, two colons,
::`. Because the constructor has the same name as the function, we get this funky syntax:Fraction::Fraction()
- We need to tell the compiler what class the function belongs to, and we do that with the class name (
Slide 22
The overloaded constructor
- We also have an overloaded constructor that takes in two values that the user sets. It is called as follows:
// create a // 1/2 fraction Fraction fracA(1,2); // create a // 4/6 fraction Fraction fracB(4,6);
- Here is the code:
// purpose: an overloaded constructor // to create a custom fraction // that immediately gets reduced // arguments: an int numerator // and an int denominator Fraction::Fraction(int num, int denom) { this->num = num; this->denom = denom; // reduce in case we were given // an unreduced fraction reduce(); }
- Notice that the paramters for our function are the same name as the class variables,
num
anddenom
.- We must use the
this
pointer to set the values so we can differentiate them. Here,this
refers to whatever instance we have, and it sets the class variables accordingly to the values of the parametesr.
- We must use the
Slide 23
Fraction
multiplication
- Let's create a couple of fractions and multiply them:
Fraction fracA(1, 2); Fraction fracB(2, 3); fracA.multiply(fracB); // fracA now holds 1/3
- The code for the
multiply
function looks like this:// purpose: to multiply another fraction // with this one, storing the result // in this fraction // arguments: other: another Fraction // return value: none void Fraction::multiply(const Fraction &other) { num *= other.num; denom *= other.denom; // reduce the fraction reduce(); }
Slide 24
reduce()
and gcd()
- We have two private functions to write that help us multiply.
- The
reduce()
function converts our fraction to a reduced one:// purpose: reduce the fraction to lowest terms void Fraction::reduce() { // find the greatest common divisor int frac_gcd = gcd(num,denom); // reduce by dividing num and denom by the gcd num = num / frac_gcd; denom = denom / frac_gcd; }
- The
gcd()
function is a clever little recursive function that quickly finds the greatest common divisor between two numbers:int Fraction::gcd(int u, int v) { if (v != 0) { return gcd(v, u % v); } else { return u; } }
Slide 25
Fraction decimal value
- The client might want to get the decimal value out of a fraction:
Fraction fracA(1, 2); double f = fracA.decimal(); cout << f << endl;
Output:
0.5
- The code for
decimal
is pretty simple:// purpose: to return the decimal value for this fraction // return value: a double representing the decimal value double Fraction::decimal() { return (double) num / denom; // must cast at least one value }
- We have to cast the numberator to a
double
, meaning that the compiler will treat it as adouble
. If we didn't do that, we would end up with integer division, which is not what we want.
Slide 26
Overloading the <<
operator
- In C++, we can make our function utilize standard operators for their own purposes. For example, we could make the less than operator for our class compare our two fractions (see the demo code for an example of the less than operator).
- Because we might want to print out our fraction in fractional form (instead of decimal form), we can overload the
<<
operator so we can use our class with<<
:// purpose: To overload the << operator // for use with cout // arguments: a reference to an outstream and the // fraction we are using // return value: a reference to the outstream ostream& operator<<(ostream& out, const Fraction &frac) { out << frac.num << "/" << frac.denom; return out; }
- Example:
Fraction frac(4, 5); cout << frac << endl;
Output:
4 / 5