We are now creating 10,000 instances of our 50MB classβ¦we will use 2GB of memory!
Let's see what will happen!
Slide 5
What happened?
The program slowed to a crawl! We used lots of memory, and kept it!
We call this problem a memory leak, and we want to avoid it. In other words: return the memory you don't need when you're done with it!
Even though each of the 10,000 class instances went out of scope, we kept all the memory, because it was dynamically allocated. Our computer started running out of memory!
We requested the memory (with new) in the constructor.
When should we return the memory (with delete)?
We want to wait until the part of our program that uses our class is done with it.
In many cases, this is when the class instance goes out of scope.
When a class instance goes out of scope, the class can clean up for itself (i.e., return all of its allocated memory) in the "destructor." (not the deconstructorβ¦).
The destructor is automatically called by the runtime when a class instance goes out of scope.
A destructor is defined with a funky syntax: ClassName::~ClassName().
Here is the destructor we need for our program:
Demo::~Demo(){delete[]bigArray;}
Now, every time our class instance goes out of scope, we return the memory to the operating system, so we never have more than 50MB used at once. No more slowing down!
Slide 6
Today's Goals:
Introduction to Pointers
What are pointers?
Pointer Syntax
Pointer Tips
Pointer Practice
Binky
Back to classes
The copy constructor
Slide 7
Introduction to Pointers
The next major topic is about the idea of a pointer in C++. We need to use pointers when we create data structures like vectors and linked lists (which we will do next week!)
Pointers are used heavily in the C language, and also in C++, though we haven't needed them yet (we briefly saw them last time when we introduced dynamic memory)
Pointers delve under the hood of C++ to the memory system, and so we must start to become familiar with how memory works in a computer.
Slide 8
Your computer's memory system
The memory in a computer can be thought of simply as a long row of boxes, with each box having a value in it, and an index associated with it.
If this sounds like an array, it's because it is!
Computer memory (particularly, Random Access Memory, or RAM) is just a giant array. The "boxes" can hold different types, but the numbers associated with each box is just a number, one after the other:
values (ints):
7
2
8
3
14
99
-6
3
45
11
index:
0
4
8
12
16
20
24
28
32
36
In C++, the location of each box is the address of the box: the address can tell us where the variable is located (like a house address).
values (strings):
cat
dog
apple
tree
shoe
hand
chair
light
cup
toe
address:
100
200
300
400
500
600
700
800
900
1000
Let's create string variable, called pet, with a value of "cat":
stringpet="cat";
We can now refer to a particular value by its address. We can imagine it like this, based on the array above:
pet:
cat
100
Based on the diagram above, what is the address of the pet variable?
It is 100.
The operating system determines the address, not you! In this case it happens to be 10 (according to our diagram), but it could be any value.
Slide 9
A pointer is just a memory address
At this point, we have a variable, and we have said that the value has an address.
If we store that memory address in a different variable, it is called a pointer.
stringpet="cat";
pet:
cat
10
Let's say we have another variable, called petPointer that looks like this:
petPointer:
100
1234
petPointer's value is a memory address.
What is a pointer? A memory address!
Slide 10
Introduction to Pointers
What is a pointer??
a memory address!
Slide 11
Introduction to Pointers
We generally use an arrow to "point" from a pointer to the address it points to:
petPointer:
100
1234
β
pet:
cat
100
We really don't care about the actual memory address numbers themselves, and most often we will simply use the visual "pointer" arrow to show that a variable points to another variable, without the numbers:
petPointer:
β
pet:
cat
Slide 12
What you need to know about pointers
Every location in memory, and therefore every variable, has an address.
Every address corresponds to a unique location in memory.
The computer knows the address of every variable in your program.
Given a memory address, the computer can find out what value is stored at that location.
While addresses are just numbers, C++ treats them as a separate type. This allows the compiler to catch cases where you accidentally assign a pointer to a numeric variable and vice versa (which is almost always an error).
Slide 13
Pointer Syntax
Pointer syntax can get tricky. We will not go too deep β you'll get that when you take cs107!
Pointer Syntax #1: To declare a pointer of a particular type, use the * (asterisk) symbol:
string*petPtr;// declare a pointer (which will hold a memory address) to a stringint*agePtr;// declare a pointer to an intchar*letterPtr;// declare a pointer to a char
The type for petPtr is a string* and not a string. This is important! A pointer type is distinct from the pointee type.
Slide 14
Pointer Syntax
Pointer Syntax #2: To get the address of another variable, use the & (ampersand) symbol. This is not a reference! It is the same symbol, but does not mean the same thing.
string*petPtr;// declare a pointer (which will hold a memory address) to a string// at this point, petPtr's value is bogus!// petPtr does have its own address, which we// will pretend is 1234
So far, here is what we have:
petPtr:
?
1234
β
?
When we declare a pointer, its value is bogus! It doesn't have anything to point to yet, so the value isn't anything useful.
Now, let's create a pet variable:
stringpet="cat";// a string variable, pretend it is at memory location 10
Now we have:
petPtr:
?
1234
β
?
pet:
cat
100
In order to get petPtr to point to pet, we have to assign the address of pet to petPtr. We do this like any other assignment, except that we use the & symbol:
petPtr=&pet;// petPtr now holds the address of pet
Finally, we have this situation:
petPtr:
10
1234
β
pet:
cat
100
Notice that petPtr's value is 10, which is the address of pet. That's because petPtr is a pointer, which means that its value is an address!
By the way: you almost never need to do this in CS106B! You'll use it more in CS 107, but if you find yourself using it in this class, double-check your reasons.
Let's look at this example in Qt Creator.
Slide 15
Pointer Syntax
Pointer Syntax #3: To get value of the variable a pointer points to, use the * (asterisk) character (in a different way than before!):
string*petPtr;// declare a pointer to a stringstringpet="cat";// a string variable, pretend it is at memory location 10petPtr=&pet;// petPtr now holds the address of petcout<<*petPtr<<endl;// prints out "cat"
When we use * in this way, we say that we are dereferencing the pointer, which follows the address to its location and gets what is at that location.
petPtr:
10
1234
β
pet:
cat
100
Slide 16
An important note: pointers are just numbers!!!!!
Throughout the history of CS106B, students have been confused by pointers. There is nothing magical about them!
Pointers are just numbers that are associated with a type. We rarely care what those numbers actually are, but they are just numbers.
You can assign a pointer to another pointer of the same type, and you're just using integers:
intx=4;// pretend x has some address, which is a numberinty;y=x;// what is y's value? It's 4;cout<<"x: "<<x<<", y:"<<y<<endl;int*xPtr;int*yPtr;xPtr=&x;// what is xPtr's value? It is the address of x, some numberyPtr=xPtr;// what is yPtr's value? It is also the address of x, the same number// we need to cast to a size_t below so we print out a regular numbercout<<"xPtr: "<<(size_t)xPtr<<", yPtr:"<<(size_t)yPtr<<endl;
(each time we run the program, we might get a different output for the second line above, but the two numbers will always be the same)
Slide 17
Pointer tips
Pointer Tip #1: To ensure that we can tell if a pointer has a valid address or not, set your declared pointer to nullptr, which means "no valid address" (it actually is just 0 in C++).
Instead of this:
string*petPtr;// declare a pointer to a string with no defined value
petPtr:
?
1234
β
?
We do this, instead:
string*petPtr=nullptr;// declare a pointer to a string that points to nullptr
petPtr:
0
1234
β
nullptr (not a valid address)
Slide 18
Pointer tips
Pointer Tip #2: If you are unsure if your pointer holds a valid address, you should check for nullptr
voidprintPetName(string*petPtr){if(petPtr!=nullptr){cout<<*petPtr<<endl;// prints out the value pointed to by petPtr// if it is not nullptr}else{cout<<"petPtr is not valid!"<<endl;}}
Slide 19
Pointer practice
The little boxes we draw to show the memory are so, so important to understanding what is happening. Always draw boxes when learning pointers!
int*nPtr=nullptr;
What type does this pointer point to?
What should we draw?
Answers:
The type that the pointer points to is an int β in other words, if we follow the pointer's value (which is an address!), we will find an int.
We should draw the following:
nPtr:
0
7224
β
nullptr
Where did the 7224 come from?
The operating system determines what that value is!
I just made up the value for this example, and you will almost never care what the address of the pointer itself is (in this class, anyway β you will care about that in CS 107!). The 7224 just tells us where nPtr is located, and we don't care!
Slide 20
Pointer Practice
Let's continue the previous example:
int*nPtr=nullptr;intn=16;
What should we draw?
We have two variables, nPtr and n. They are not related to each other in any way yet:
nPtr:
0
7224
β
nullptr
n:
16
5432
I made up the number 5432 because we don't know (or care) what it is.
Let's add one more line:
int*nPtr=nullptr;intn=16;nPtr=&n;
What should we do now?
We update the value of nPtr to the address of n:
nPtr:
5432
7224
β
n:
16
5432
We now say that nPtrpoints ton.
Slide 21
Introduction to Pointers
What is a pointer??
a memory address!
Slide 22
Pointer Practice
Let's look at the following code, and draw our pictures:
This code dereferencessPtr1, goes to the location it points to, and sets the value of that string to goodbye. What do we have now?
sPtr1:
9988
6420
β
s:
goodbye
9988
sPtr2:
9988
2232
βοΈ
Because both pointers point to the same location, they both end up print out the same thing!
goodbye
goodbye
Slide 26
Introduction to Pointers
What is a pointer??
a memory address!
Slide 27
More information about addresses
Addresses are just numbers, as we have seen. However, you will often see an address listed like this:
0x7fff3889b4b4
or this:
0x602a10
This is a base-16, hexadecimal representation. The 0x just means "the following number is in hexadecimal notation."
The letters are used because base 16 needs 16 digits:
0123456789abcdef
This is a base-16, or "hexadecimal" representation. The 0x just means "the following number is in hexadecimal."
The letters are used because base 16 needs 16 digits:
0 1 2 3 4 5 6 7 8 9 a b c d e f
So, you might see the following β remember, we don't care about address values, just that they are memory locations:
sPtr1:
0xfcab0
0xfe01a
β
s:
goodbye
0xfcab0
sPtr2:
0xfcab0
0xfe5c2
βοΈ
Slide 28
Binky
In 1999 (!), a certain Nick Parlante produced a little stop-animation video, called Pointer Fun with Binky
Here it is in all its glory:
Slide 29
If we have time: Copy Constructors
Let's take a look at a simple Rectangle class to demonstrate a problem that we need to be aware of.
Here is the header file, rectangle.h, for the program:
#pragma once
classRectangle{public:Rectangle(doublewidth=1,doubleheight=1);// constructor~Rectangle();// destructordoublearea();doubleperimeter();doublegetHeight();doublegetWidth();private:double*height;// pointer to a doubledouble*width;// pointer to a double};
intmain(){Rectangler(3,4);cout<<"Width: "<<r.getWidth()<<", ";cout<<"Height: "<<r.getHeight()<<endl;cout<<"Area: "<<r.area()<<endl;cout<<"Perimeter: "<<r.perimeter()<<endl;// let's make a copy:Rectangler2=r;// THIS CRASHES!!!return0;}
What happened? Why did we crash?
We need to dig under the hood
Let's look at what the following two lines do:
Rectangler(3,4);Rectangler2=r;
The two private variables we have are int* pointers, width and height. Becuase we use new to allocate space for the data for those pointers, we don't have variables with names for the data, but the data is still in memory. This is r:
r.width:
0x99
0x61
β
3
0x99
r.height:
0x9f
0x63
β
4
0x9f
When you make an assignment of one class instance to another in C++, the default thing to do is to construct the class and simply copy over the data in the class variables. That would be width and height, which are pointers.
That means that what gets copied are two addressese width and height, which are pointers.
That means that what gets copied are two addresses. Below, we have r on the left, and r2 on the right:
r.width:
0x99
0x61
β
3
0x99
β
r2.width:
0x99
0x65
r.height:
0x9f
0x63
β
4
0x9f
β
r2.height:
0x9f
0x67
Notice that both r.width and r2.width point to the same pointee, and likewise for r.height and r2.height. This is bad news! Now, when the destructor gets called, both will try to delete the value that was allocated, and this isn't allowed! The program crashes!
What do we do? We define a copy constructor that tells the compiler how to copy our class. Without it, it will simply copy the values in the class variables, which isn't what we want in this case.
Here is the copy constructor definition in rectangle.h:
Here is the copy constructor itself. We need to allocate new memory for the new data, then we assign the old data to our new copy. This goes into rectangle.cpp:
Rectangle::Rectangle(constRectangle&src){// copy constructorwidth=newdouble;// request new memoryheight=newdouble;// copy the values*width=*src.width;*height=*src.height;}