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

Lecture15 Slides

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

Lecture15 Slides

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

Object-Oriented

Programming
What do you think makes a good, well-designed
abstraction?
(put your answers the chat)
Object-Oriented
Roadmap Programming

C++ basics
Implementation
User/client
vectors + grids arrays

dynamic memory
stacks + queues
management
sets + maps linked data structures

real-world
Diagnostic algorithms

Life after CS106B!


Core algorithmic recursive
Tools testing analysis problem-solving
Object-Oriented
Roadmap Programming

C++ basics
Implementation
User/client
vectors + grids arrays

dynamic memory
stacks + queues
management
sets + maps linked data structures

real-world
Diagnostic algorithms

Life after CS106B!


Core algorithmic recursive
Tools testing analysis problem-solving
How do we design and
Today’s define our own
question abstractions?
1. Review
Today’s
2. What is a class?
topics
3. Designing C++ classes

4. Writing classes in C++


Review
Two types of recursion
Basic recursion Backtracking recursion

● One repeated task that builds up ● Build up many possible solutions


a solution as you come back up through multiple recursive calls at
the call stack each step
● The final base case defines the ● Seed the initial recursive call with
initial seed of the solution and an “empty” solution
each call contributes a little bit to ● At each base case, you have a
the solution potential solution
● Initial call to recursive function
produces final solution
Backtracking recursion: Exploring many possible solutions
Overall paradigm: choose/explore/unchoose

Two ways of doing it Three use cases for backtracking


● Choose explore undo 1. Generate/count all solutions
○ Uses pass by reference; usually with
large data structures (enumeration)
○ Explicit unchoose step by "undoing" 2. Find one solution (or prove
prior modifications to structure
○ E.g. Generating subsets (one set existence)
passed around by reference to track 3. Pick one best solution
subsets)
● Copy edit explore
○ Pass by value; usually when memory General examples of things you can do:
constraints aren’t an issue - Permutations
○ Implicit unchoose step by virtue of - Subsets
making edits to copy - Combinations
○ E.g. Building up a string over time - etc.
We’ve seen lots of different backtracking strategies...
Questions to ask yourself when planning your strategy:

● What does my decision tree look like? (decisions, options, what to keep track of)
● What are our base and recursive cases?
● What’s the provided function prototype and requirements? Do we need a helper
function?
● Do we care about returning or keeping track of the path we took to get to our solution?
● Which of our three use cases does our problem fall into? (generate/count all solutions,
find one solution/prove its existence, pick one best solution)
● What are we returning as our solution? (a boolean, a final value, a set of results, etc.)
● What are we building up as our “many possibilities” in order to find our solution?
(subsets, permutations, combinations, or something else)
Where are we now?
classes
object-oriented programming

arrays

abstract data structures dynamic memory


(vectors, maps, etc.) management

linked data structures

testing algorithmic analysis recursive problem-solving


classes
object-oriented programming

arrays

abstract data structures dynamic memory


(vectors, maps, etc.) management

linked data structures

testing algorithmic analysis recursive problem-solving


classes
object-oriented programming

arrays

abstract data structures dynamic memory


(vectors, maps, etc.) management

linked data structures

testing algorithmic analysis recursive problem-solving


classes
object-oriented programming

This is our abstraction


boundary!
arrays

abstract data structures dynamic memory


(vectors, maps, etc.) management

linked data structures

testing algorithmic analysis recursive problem-solving


Revisiting abstraction
Example
demonstration
borrowed from Keith
Schwarz
Definition
abstraction
Design that hides the details of how
something works while still allowing the user
to access complex functionality
How do we accomplish this in
C++? With classes!

Definition
abstraction
Design that hides the details of how
something works while still allowing the user
to access complex functionality
What is a class?
Definition
class
A class defines a new data type for our
programs to use.
Definition
class
A class defines a new data type for our
programs to use.

This sounds familiar...


Remember structs?
struct BackpackItem {
int survivalValue;
int weight;
};

struct Juror {
string name;
int bias;
};
Remember structs?
struct BackpackItem {
int survivalValue; Definition
int weight;
}; struct
A way to bundle different
struct Juror { types of information in C++ –
string name; like creating a custom data
int bias; structure.
};
Then what’s the difference between a class and a struct?
Remember structs?
GridLocation chosen; GPoint origin(0, 0);
cout << chosen.row << endl; cout << origin.getX() << endl;
cout << chosen.col << endl; cout << origin.getY() << endl;

What’s the difference in how you use a GridLocation vs. a GPoint?


Remember structs?
GridLocation chosen; GPoint origin(0, 0);
cout << chosen.row << endl; cout << origin.getX() << endl;
cout << chosen.col << endl; cout << origin.getY() << endl;

chosen.row = 3; origin.x = 3;
chosen.col = 4; origin.y = 4;

What’s the difference in how you use a GridLocation vs. a GPoint?


Remember structs?
GridLocation chosen; GPoint origin(0, 0);
cout << chosen.row << endl; cout << origin.getX() << endl;
cout << chosen.col << endl; cout << origin.getY() << endl;

chosen.row = 3; origin.x = 3;
chosen.col = 4; origin.y = 4;

We don’t have direct access to GPoint’s x and y coordinates!


What is a class?
● Examples of classes we’ve already seen: Vectors, Maps, Stacks, Queues
What is a class?
● Examples of classes we’ve already seen: Vectors, Maps, Stacks, Queues

● Every class has two parts:


○ an interface specifying what operations can be performed on instances of
the class (this defines the abstraction boundary)
○ an implementation specifying how those operations are to be performed
What is a class?
● Examples of classes we’ve already seen: Vectors, Maps, Stacks, Queues

● Every class has two parts:


○ an interface specifying what operations can be performed on instances of
the class (this defines the abstraction boundary)
○ an implementation specifying how those operations are to be performed

● The only difference between structs + classes are the encapsulation defaults.
○ A struct defaults to public members (accessible outside the class itself).
○ A class defaults to private members (accessible only inside the class
implementation).
Definition
encapsulation
The process of grouping related information
and relevant functions into one unit and
defining where that information is accessible
Another way to think about classes...
● A blueprint for a new type of C++ object!
Another way to think about classes...
● A blueprint for a new type of C++ object!
○ The blueprint describes a general structure, and we can create
specific instances of our class using this structure.
Another way to think about classes...
● A blueprint for a new type of C++ object!
○ The blueprint describes a general structure, and we can create
specific instances of our class using this structure.

Definition
instance
When we create an object that is our new type,
we call this creating an instance of our class.
Another way to think about classes...
● A blueprint for a new type of C++ object!
○ The blueprint describes a general structure, and we can create
specific instances of our class using this structure.

Vector<int> vec;

Creates an instance of the Vector class


(i.e. an object of the type Vector)
How do we design C++
classes?
Three main parts
● Member variables

● Member functions (methods)

● Constructor
Three main parts
● Member variables
○ These are the variables stored within the class
○ Usually not accessible outside the class implementation

● Member functions (methods)

● Constructor
Three main parts
● Member variables

● Member functions (methods)


○ Functions you can call on the object
○ E.g. vec.add(), vec.size(), vec.remove(), etc.

● Constructor
Three main parts
● Member variables

● Member functions (methods)

● Constructor
○ Gets called when you create the object
○ E.g. Vector<int> vec;
Three main parts
● Member variables
○ These are the variables stored within the class
○ Usually not accessible outside the class implementation

● Member functions (methods)


○ Functions you can call on the object
○ E.g. vec.add(), vec.size(), vec.remove(), etc.

● Constructor
○ Gets called when you create the object
○ E.g. Vector<int> vec;
How do we design a class?
We must specify the 3 parts:

1. Member variables: What subvariables make up this new variable type?

2. Member functions: What functions can you call on a variable of this


type?

3. Constructor: What happens when you make a new instance of this


type?
How do we design a class?
We must specify the 3 parts:

1. Member variables: What subvariables make up this new variable type?

2. Member functions: What functions can you call on a variable of this


type?

3. Constructor: What happens when you make a new instance of this


type?
In general, classes are useful in helping us with complex programs where
information can be grouped into objects.
Breakout design
activity
How would you design a class for...
We must specify the 3 parts:

● A bank account that enables 1. Member variables: What subvariables


transferring funds between make up this new variable type?
accounts
2. Member functions: What functions can
you call on a variable of this type?
● A Spotify (or other music
platform) playlist
3. Constructor: What happens when you
make a new instance of this type?
Announcements
Announcements
● The mid-quarter diagnostic is coming soon! Make sure to read through the
information on the linked page if you haven't yet.
○ The link to access your personalized diagnostic access portal will be posted on the homepage
of the website at 12:01am PDT Friday and will remain up until 11:59pm PDT Sunday.

● Assignment 3 is due tomorrow, Thursday, July 16 at 11:59pm.

● There will be a diagnostic review session hosted by Trip tomorrow night, from
7-8:30pm. The session will be recorded and made available on Canvas shortly
afterwards.
How do we write classes in
C++?
Random Bags
Random Bags
● A random bag is a data structure similar to a stack or queue. It
supports two operations:
○ add, which puts an element into the random bag, and
○ remove random, which returns and removes a random element from the bag.
Random Bags
● A random bag is a data structure similar to a stack or queue. It
supports two operations:
○ add, which puts an element into the random bag, and
○ remove random, which returns and removes a random element from the bag.

● Random bags have a number of applications:


○ Simpler: Shuffling a deck of cards.
○ More advanced: Generating artwork, designing mazes, and training self-driving cars
to park and change lanes!
Random Bags
● A random bag is a data structure similar to a stack or queue. It
supports two operations:
○ add, which puts an element into the random bag, and
○ remove random, which returns and removes a random element from the bag.

● Random bags have a number of applications:


○ Simpler: Shuffling a deck of cards.
○ More advanced: Generating artwork, designing mazes, and training self-driving cars
to park and change lanes.

● Let’s go create our own custom RandomBag type!


Creating our own class
Classes in C++
● Defining a class in C++ (typically) requires two steps:
Classes in C++
● Defining a class in C++ (typically) requires two steps:
○ Create a header file (typically suffixed with .h) describing what
operations the class can perform and what internal state it needs.
Classes in C++
● Defining a class in C++ (typically) requires two steps:
○ Create a header file (typically suffixed with .h) describing what
operations the class can perform and what internal state it needs.
○ Create an implementation file (typically suffixed with .cpp) that
contains the implementation of the class.
Classes in C++
● Defining a class in C++ (typically) requires two steps:
○ Create a header file (typically suffixed with .h) describing what
operations the class can perform and what internal state it needs.
○ Create an implementation file (typically suffixed with .cpp) that
contains the implementation of the class.

● Clients of the class can then include (using the #include directive)
the header file to use the class.
Header files
What's in a header?
What's in a header?
#pragma once
This boilerplate code is called a
preprocessor directive. It’s used to
make sure weird things don’t
happen if you include the same
header twice.

Curious how it works? Come ask us


after class!
What's in a header?
#pragma once

class RandomBag {

This is a class definition. We’re


creating a new class called
RandomBag. Like a struct, this
defines the name of a new type
that we can use in our programs.
};
What's in a header?
#pragma once

class RandomBag {

Don't forget to add the semicolon!

You'll run into some scary compiler


}; errors if you leave it out!
What's in a header?
#pragma once

class RandomBag {
public:

private:

};
The public interface specifies what
What's in a header? functions you can call on objects
of this type.
#pragma once

class RandomBag { Think things like the Vector


public: .add() function or the string's
.find().

private:

};
The public interface specifies what
What's in a header? functions you can call on objects
of this type.
#pragma once

class RandomBag { Think things like the Vector


public: .add() function or the string's
.find().

The private implementation


private: contains information that objects
of this class type will need in order
};
to do their job properly. This is
invisible to people using the class.
What's in a header?
#pragma once
These are member functions of
class RandomBag { the RandomBag class. They're
public: functions you can call on objects
void add(int value); of type RandomBag.
int removeRandom();
All member functions must be
private:
defined in the class definition. We'll
implement these functions in the
}; C++ file.
What's in a header?
#pragma once
#include "vector.h" This is a data member of the
class RandomBag {
public:
class. This tells us how the class is
void add(int value); implemented. Internally, we're
int removeRandom(); going to store a Vector<int>
holding all the elements. The only
code that can access or touch this
Vector is the RandomBag
private:
Vector<int> elems;
};
implementation.
Header summary
#pragma once
#include "vector.h"
class RandomBag { Class definition and name
public:
void add(int value);
int removeRandom(); Methods

private:
Vector<int> elems; Member variable
};
Header summary
#pragma once
#include "vector.h"
class RandomBag {
public:
void add(int value);
int removeRandom();

private:
Vector<int> elems;
};
Implementation files
RandomBag.cpp
#include "RandomBag.h"
#include "RandomBag.h"
If we're going to implement the
RandomBag type, the .cpp file
needs to have the class definition
available. All implementation files
need to include the relevant
headers.
#include "RandomBag.h"
If we're going to implement the
RandomBag type, the .cpp file
needs to have the class definition
available. All implementation files
need to include the relevant
headers.
#pragma once
#include "vector.h"
class RandomBag {
public:
void add(int value);
int removeRandom();

private:
Vector<int> elems;
};
#include "RandomBag.h"

#pragma once
#include "vector.h"
class RandomBag {
public:
void add(int value);
int removeRandom();

private:
Vector<int> elems;
};
#include "RandomBag.h"

void RandomBag::add(int value){


elems.add(value);
}

#pragma once
#include "vector.h"
class RandomBag {
public:
void add(int value);
int removeRandom();

private:
Vector<int> elems;
};
#include "RandomBag.h"

void RandomBag::add(int value){


elems.add(value);
}
The syntax RandomBag::add means “the add function defined inside
of RandomBag." The :: operator is called the scope resolution operator
in C++ and is used to say where to look for things.

#pragma once
#include "vector.h"
class RandomBag {
public:
void add(int value);
int removeRandom();

private:
Vector<int> elems;
};
#include "RandomBag.h"

void RandomBag::add(int value){


elems.add(value);
}
If we had written something like this instead, then the compiler
would think we were just making a free function named add that has
nothing to do with RandomBag’s version of add. That’s an easy
mistake to make!
#pragma once
#include "vector.h"
class RandomBag {
public:
void add(int value);
int removeRandom();

private:
Vector<int> elems;
};
#include "RandomBag.h"

void RandomBag::add(int value){


elems.add(value);
}
We don't need to specify where elems is. The compiler knows that
we're inside RandomBag, and so it knows that this means "the current
RandomBag's collection of elements."

#pragma once
#include "vector.h"
class RandomBag {
public:
void add(int value);
int removeRandom();

private:
Vector<int> elems;
};
#include "RandomBag.h"

void RandomBag::add(int value){


elems.add(value);
}

int RandomBag::removeRandom() {
if (elems.isEmpty()) {
error("Aaaaahhh!");
}
int index = randomInteger(0, elems.size() - 1);
int result = elems[index];
#pragma once
elems.remove(index);
#include "vector.h"
return result;
class RandomBag {
}
public:
void add(int value);
int removeRandom();

private:
Vector<int> elems;
};
#include "RandomBag.h"

void RandomBag::add(int value){


elems.add(value);
}

int RandomBag::removeRandom() {
if (elems.isEmpty()) {
error("Aaaaahhh!");
}
int index = randomInteger(0, elems.size() - 1);
int result = elems[index];
#pragma once
elems.remove(index);
#include "vector.h"
return result;
class RandomBag {
}
public:
void add(int value);
int removeRandom();
int size();
bool isEmpty();
private:
Vector<int> elems;
};
#include "RandomBag.h"

void RandomBag::add(int value){


elems.add(value);
}

int RandomBag::removeRandom() {
if (elems.isEmpty()) {
error("Aaaaahhh!");
}
int index = randomInteger(0, elems.size() - 1);
int result = elems[index];
#pragma once
elems.remove(index);
#include "vector.h"
return result;
class RandomBag {
}
public:
void add(int value);
int RandomBag::size() {
int removeRandom();
return elems.size();
int size();
}
bool isEmpty();
private:
Vector<int> elems;
};
#include "RandomBag.h"

void RandomBag::add(int value){


elems.add(value);
}

int RandomBag::removeRandom() {
if (elems.isEmpty()) {
error("Aaaaahhh!");
}
int index = randomInteger(0, elems.size() - 1);
int result = elems[index];
#pragma once
elems.remove(index);
#include "vector.h"
return result;
class RandomBag {
}
public:
void add(int value);
int RandomBag::size() {
int removeRandom();
return elems.size();
int size();
}
bool isEmpty();
private:
bool RandomBag::isEmpty() {
Vector<int> elems;
return size() == 0;
};
}
#include "RandomBag.h"

void RandomBag::add(int value){


elems.add(value);
}

int RandomBag::removeRandom() {
if (elems.isEmpty()) {
error("Aaaaahhh!");
}
int index = randomInteger(0, elems.size() - 1);
int result = elems[index];
#pragma once
elems.remove(index);
#include "vector.h"
return result; This code calls our own class RandomBag {
}
size() function. The class public:
void add(int value);
int RandomBag::size() { implementation can use the int removeRandom();
return elems.size();
} public interface. int size();
bool isEmpty();
private:
bool RandomBag::isEmpty() {
Vector<int> elems;
return size() == 0;
};
}
#include "RandomBag.h"

void RandomBag::add(int value){


elems.add(value);
What a good idea!
}

int RandomBag::removeRandom() { Let's use it up here


if (elems.isEmpty()) {
error("Aaaaahhh!"); as well.
}
int index = randomInteger(0, size() - 1);
int result = elems[index];
#pragma once
elems.remove(index);
#include "vector.h"
return result;
class RandomBag {
}
public:
void add(int value);
int RandomBag::size() {
int removeRandom();
return elems.size();
int size();
}
bool isEmpty();
private:
bool RandomBag::isEmpty() {
Vector<int> elems;
return size() == 0;
};
}
#include "RandomBag.h"

void RandomBag::add(int value){


elems.add(value);
}

int RandomBag::removeRandom() {
if (elems.isEmpty()) {
error("Aaaaahhh!"); This use of the const keyword
}
int index = randomInteger(0, size() - 1); means "I promise that this
int result = elems[index]; function doesn't once
#pragma change the
elems.remove(index);
return result; state of#include
the object."
"vector.h"
class RandomBag {
}
public:
void add(int value);
int RandomBag::size() {
int removeRandom();
return elems.size();
int size() const;
}
bool isEmpty() const;
private:
bool RandomBag::isEmpty() {
Vector<int> elems;
return size() == 0;
};
}
#include "RandomBag.h"

void RandomBag::add(int value){


elems.add(value);
}

int RandomBag::removeRandom() {
if (elems.isEmpty()) {
error("Aaaaahhh!");
}
We have to remember to
int index = randomInteger(0, size() - 1);
int result = elems[index];
add it into the
elems.remove(index);
#pragma once
#include "vector.h"
implementation as well!
return result;
class RandomBag {
}
public:
void add(int value);
int RandomBag::size() const {
int removeRandom();
return elems.size();
int size() const;
}
bool isEmpty() const;
private:
bool RandomBag::isEmpty() const {
Vector<int> elems;
return size() == 0;
};
}
#include "RandomBag.h"

void RandomBag::add(int value){


elems.add(value);
}

int RandomBag::removeRandom() {
if (elems.isEmpty()) {
error("Aaaaahhh!");
}
int index = randomInteger(0, size() - 1);
int result = elems[index];
#pragma once
elems.remove(index);
#include "vector.h"
return result;
class RandomBag {
}
public:
void add(int value);
int RandomBag::size() const {
int removeRandom();
return elems.size();
int size() const;
}
bool isEmpty() const;
private:
bool RandomBag::isEmpty() const {
Vector<int> elems;
return size() == 0;
};
}
Using a custom class
[Qt Creator demo]
Takeaways
● Public member variables declared in the header file are automatically
accessible in the .cpp file
Takeaways
● Public member variables declared in the header file are automatically
accessible in the .cpp file

● As a best practice, member variables should be private, and you can create
public member functions to allow users to edit them
Takeaways
● Public member variables declared in the header file are automatically
accessible in the .cpp file

● As a best practice, member variables should be private, and you can create
public member functions to allow users to edit them

● Member functions have an implicit parameter that allows them to know what
object they’re operating on
Takeaways
● Public member variables declared in the header file are automatically
accessible in the .cpp file

● As a best practice, member variables should be private, and you can create
public member functions to allow users to edit them

● Member functions have an implicit parameter that allows them to know what
object they’re operating on

● When you don’t have a constructor, there’s a default 0 argument constructor


that instantiates all private member variables
○ (We’ll see an explicit constructor tomorrow!)
An example:
Structs vs. classes
[time-permitting]
Summary
Object-Oriented Programming
● We create our own abstractions for defining data types using classes. Classes
allow us to encapsulate information in a structured way.

● Classes have three main parts to keep in mind when designing them:
○ Member variables → these are always private
○ Member functions (methods)
○ Constructor → this is created by default if you don’t define one

● Writing classes requires the creation of a header (.h) file for the interface and
an implementation (.cpp) file.
What’s next?
Object-Oriented
Roadmap Programming

C++ basics
Implementation
User/client
vectors + grids arrays

dynamic memory
stacks + queues
management
sets + maps linked data structures

real-world
Diagnostic algorithms

Life after CS106B!


Core algorithmic recursive
Tools testing analysis problem-solving
Object-Oriented
Roadmap Programming

C++ basics
Implementation
User/client
vectors + grids arrays

dynamic memory
stacks + queues
management
sets + maps linked data structures

real-world
Diagnostic algorithms

Life after CS106B!


Core algorithmic recursive
Tools testing analysis problem-solving
Dynamic memory and arrays

You might also like