Top Banner
1 Lecture #3 A Quick Review of Pointers Dynamic Memory Allocation The “this” pointer! Resource Management Part 1 Copy Constructors Note: We’re only reviewing about 50% of the key pointer concepts! If you feel uncomfortable with pointers, then study and become an expert before our next class!
80

Lecture #3

Feb 04, 2016

Download

Documents

yonah

Lecture #3. A Quick Review of Pointers Dynamic Memory Allocation Resource Management Part 1 Copy Constructors. Note: We’re only reviewing about 50% of the key pointer concepts! If you feel uncomfortable with pointers, then study and become an expert before our next class!. Let’s Play…. - PowerPoint PPT Presentation
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Lecture #3

1

Lecture #3• A Quick Review of Pointers• Dynamic Memory Allocation• The “this” pointer!

• Resource Management Part 1– Copy Constructors

Note: We’re only reviewing about 50% of the key pointer concepts!

If you feel uncomfortable with pointers, then study and become an expert before our next class!

Page 2: Lecture #3

2

Let’s Play….

Programming Language Inventor

Or

Serial Killer

See if you can guess who uses a keyboard and who uses a chainsaw!

Page 3: Lecture #3

3 Every Variable has an Address

You can think of the computer’s memory like a street with a bunch of vacant lots.

Some variables occupy a single lot, while others occupy several adjacent lots.

100 101 102 103 104

MEMORY

void foo(){ short students = 5; char grade = ‘B’;...

students

5

grade

‘B’

When you define a variable in your program, the computer finds an unused

address in memory and reserves it for your variable.

Page 4: Lecture #3

4

Variable Addresses

Important: The address of a variable is defined to be the lowest address in memory where the variable is stored.

0000000000000001

000010000000100100001002000010030000100400001005000010060000100700001008000010090000101000001011

999999909999999199999992

So, what is age’s address in memory?

age

41

grade 66

What about grade’s address?

void foo(){ int age = 41; char grade = ‘A’;...

Page 5: Lecture #3

5

We can get the address of a variable using the & operator.

Getting the Address of a Variable

010000100101002010030100401005010060100701008010090101001011

age

41

grade ‘B’

int main(){ int age = 41; char grade = ‘B’;

cout << “age’s address: “<< &age ; cout << “grade’s address: “ << &grade ;}

If you place an & before a variable in a program statement, it means “give me the numerical address of

the variable.”

Output:age’s address: 1006grade’s address: 1011

MEMORY

Page 6: Lecture #3

6

Ok, So What’s a Pointer?

chickens

p

...0000100000001002000010040000100600001008000010100000101200001014

...

MEMORY

int foo(){

int chickens;

chickens = 5;

5

I’m a regular variable… and I hold a regular

value!

pointer p;

p = &chickens;

I’m a pointer variable… and all I can hold are the

addresses of other variables!

1000For instance, right

now I hold a value of 1000, the

address of the “chickens” variable.

A pointer variable is a special kind of variable that holds another variable’s

address instead of a regular value.

Regular variables (like floats and ints) just hold simple values like 3.14159 or

42.

Another way to say this is:

“p points to address 1000”

Page 7: Lecture #3

7

int foo(){

int chickens;

chickens = 5;

p = &chickens;

Defining a Pointer VariableActually… This isn’t the correct way

to define a pointer variable.

When you define a pointer variable, you have to tell C++ what type of variable it’s going to point to.

int

Oh, and in C++, you use the *

symbol instead of the word “pointer”:

pointer p; * p;

Now we know the proper syntax to define a pointer

variable!

I’d like p to hold the address of (point to)

the chickens variable.

And chickens is an int

variable…So this now tells C++ that the p pointer variable will be used

to point at int variables.

To understand the type of your new variable, simply read your declaration from

right to left…

“Variable p is a pointer to

an int variable.”

Page 8: Lecture #3

8

Why do we have to tell C++ what type of variable our pointer points to? Who cares?

Why Specify the Type?

int foo(){

int chickens;

chickens = 5;

p = &chickens;

int *p;

Why the heck do we need

this?

Well, would you tell your veterinarian what kind of pet you had before he performed surgery?

Of course you would – you have to know *what* type of thing you’re pointing at before you can operate on it!

?

Page 9: Lecture #3

9

Back to Memory LaneSo in our computer’s memory, we store variables.

MEMORY

1000 1001 1002 1003

But they’re all just variables!

And some variables hold regular values (like ints).

While other variables hold address values.

int foo(){

short chickens = 5;

chickens 5

short *ptr = &chickens;

ptr1001

1004 1005 1006 1007

Finally, every variable in memory has an address!

cout << chickens;cout << ptr;

cout << &chickens;cout << &ptr;

Cool huh?// prints 5// prints 1001

// prints 1001// prints 1004

Page 10: Lecture #3

10

What do I do with Pointers?

Answer: You can use your pointer and the star operator to

read/write the other variable.

int main(void){ int var = 1234;

int *ptr;

ptr = &var;

0000100000001002000010040000100600001008000010100000101200001014

...

var

ptr1002

*ptr = 5;

If placed in front of a pointer, the * operator allows us to read/write the variable pointed-to by the pointer.

Question: So I have a pointer variable that points to another

variable… now what?

1234

cout << *ptr;

“Get the address value stored in the ptr

variable…

Then go to that address in memory…

and give me the value stored there.”

1002;Neat! The pointer lets us

indirectly access a value in memory without referring

to its variable name!

I just changed var’s value to

5…

1234;

…without ever referring to its variable name!

// cout << *ptr cout << *1002 cout << 1234

1002

1002

1234“Get the address value stored in the ptr

variable…

Then go to that address in memory…

and store a value of 5 there.”

1002

5 // *ptr = 5 *1002 = 5

Page 11: Lecture #3

11

void set(int *pa) { *pa = 5;} int main(){ int x = 1;  set(&x);  cout << x;}

0000000000000001

0000924000009241000092420000924300009244000092450000924600009247000092480000924900009250

x 1

pa 9240

5

Another Pointer Example

9240

Cool – that works! We can use pointers to modify variables from other

functions!

Let’s use pointers to modify a variable inside of another

function.

// prints 5

“Store a value of 5 at location .”

9240

Page 12: Lecture #3

12

void set(int *pa) { *pa = 5;} int main(){ int x = 1;  set(&x);  cout << x;}

What if We Didn’t Use Pointers?

Now what would happen if we didn’t use pointers in our

code?

0000000000000001

0000924000009241000092420000924300009244000092450000924600009247000092480000924900009250

x 11

pa 1 5Oh no! We tried to change the value of x

in set but it only changed the local variable!

Had we used a pointer, it would have worked!

// prints 1

Page 13: Lecture #3

13

0000000000000001

0000924000009241000092420000924300009244000092450000924600009247000092480000924900009250

When you pass a variable by reference to a function, what really

happens?

Pointers vs References

void set(int &val) // val is a ref { val = 5;} 

int main(){ int x = 1; 

set(x);  cout << x;}

In fact, a reference is just a simpler notation for passing by a pointer!

x

It looks like we’re just passing the value of x,

but in fact…

Since the set function accepts a reference…

This line is really passing the address of

variable x to set…

9240

(Under the hood, C++ uses a pointer)

val 9240

Since val points to our original variable, x,

this line actually changes x!

5

1

Page 14: Lecture #3

14

Your "nature" pictures?Operating system??

What Happens Here?int main(){ int *iptr; 

0000000000000001

0000924000009241000092420000924300009244000092450000924600009247000092480000924900009250

iptr

What address does iptr hold?

?????

Who knows??? Since the programmer didn’t initialize it, it points to some

random spot in memory!

CRASH!

Moral: You must always set the value

of a pointer variable before using the * operator on it!

*iptr = 123456; // #1 mistake!}

int someVar;iptr = &someVar; // now iptr is valid!

// A-OK now!

123456

Page 15: Lecture #3

15

Class ChallengeWrite a function called swap that accepts twopointers to integers and swaps the two values

pointed to by the pointers.

int main(void){ int a=5, b=6;

swap(&a, &b); cout << a; // prints 6; cout << b; // prints 5}

Prize: 3 prize tickets (and maybe some

candy)

Hint: Make sure you never use a pointer

unless you point it to a variable first!!!

Page 16: Lecture #3

16

Class Challenge Solution

void swap (int *pa, int *pb){ int temp; temp = *pa; *pa = *pb; *pb = temp;}

int main(){ int a=5, b=6; swap(&a,&b); cout << a; cout << b;}

0000000000000001

0000924000009242000092440000924600009248000092500000925200009254000092560000925800009260

a 5

b 69240, 92449240 9244

pa 9240

pb 9244

temp -23

Get the value stored at location

9240…and put it in temp.

5

Get the value stored at

location 9244…

And put it into location

9240.

6

Get the value stored in temp and store it in

location 9244…

Page 17: Lecture #3

17

Wrong Challenge Solution #1

void swap (int *pa, int *pb){ int *temp; *temp = *pa; *pa = *pb; *pb = temp;}

int main(){ int a=5, b=6; swap(&a,&b); cout << a; cout << b;}

0000000000000001

0000924000009242000092440000924600009248000092500000925200009254000092560000925800009260

a 5

b 69240, 92449240 9244

pa 9240

pb 9244

temp 50000

Get the value stored at

location 9240…

And put it in location

50000!

5

CRASH!

Problem: In this solution,we use a pointer without first

pointing it at a variable!

Page 18: Lecture #3

18

Wrong Challenge Solution #2

void swap (int *pa, int *pb){ int *temp; temp = pa; pa = pb; pb = temp;}

int main(){ int a=5, b=6; swap(&a,&b); cout << a; cout << b;}

0000000000000001

0000924000009242000092440000924600009248000092500000925200009254000092560000925800009260

a 5

b 69240, 92449240 9244

pa 9240

pb 9244

temp 50000

9240

9244

9240

// prints 5// prints 6

Problem: In this solution,we swap the pointers but not

the values they point to!

Page 19: Lecture #3

19

Arrays, Addresses and Pointers

int main(){ int nums[3] = {10,20,30};

}

Just like any other variable, every array has an address in memory and you can get it with the & operator.

…0000924000009242000092440000924600009248000092500000925200009254000092560000925800009260

10

20

30

nums

[0]

[1]

[2]

cout << &nums;// prints 9242

But… in C++ you don’t even need to use the & operator to

get an array’s address!

cout << nums;// also prints 9242

You can simply write the array’s name (without brackets) and C++ will give you the array’s

address! 92429242

And here’s how to make a pointer point to an array…

int *ptr = nums; // pointer to array

ptr

9242

Question: So is “nums” an address or a pointer or what?

Answer: “nums” is just an array. But C++ lets you get its address

without using the & so it looks like a pointer…

ptr is a pointer variable.

Why? Because it’s a variable that holds an

address value!

nums is just an array.

It holds three regular integer values.

But it doesn’t hold an address like a pointer variable, so it’s not a

pointer!

Page 20: Lecture #3

20

Arrays, Addresses and Pointers

int main(){ int nums[3] = {10,20,30};

}

In C++, a pointer to an array can be used just as if it were an array itself!

…0000924000009242000092440000924600009248000092500000925200009254000092560000925800009260

10

20

30

nums

[0]

[1]

[2]int *ptr = nums; // pointer to array

ptr

cout << ptr[2];

9242

// prints nums[2] or 30

Or you can use the * operator with your pointer to access the

array’s contents.

cout << *ptr; // prints nums[0] or 10

cout << *(ptr+2); // prints nums[2] or 30

Since ptr points to the top of the nums array,

this prints out the value that is two

integer elements from the top of the nums

array.

Since ptr points to the top of the nums array, this

prints out the value that is at address 9242 – i.e., the first element of the nums

array.

This line says “print out the item that is two elements down from where ptr points.” (in this

case, ptr points to the top of the array)

It’s the same as printing out nums[2] or ptr[2].

In C++, the two syntaxes have identical behavior:

ptr[j] *(ptr + j)

They both mean: “Go to the address in ptr, then skip down j elements and get the value.”

They both mean: “Go to the address in ptr, then skip down j elements and get the value.”

They both mean: “Go to the address in ptr, then skip down j elements and get the value.”

NOTE: when we say “skip down j elements,” we don’t just mean “skip

down j bytes!”

Instead we mean, skip over j of the actual elements/values in the array

(e.g., skip over the values 10 and 20 to get to 30)

Page 21: Lecture #3

21

array

Pointer Arithmetic and Arrays

void printData(int array[ ]){ cout << array[0] << “\n”; cout << array[1] << “\n”;}int main(){ int nums[3] = {10,20,30};

printData(nums);

printData(&nums[1]);

printData(nums+1);}

Did you know that when you pass an array to a function…

You’re really just passing the address to the start of the

array!… not the array itself!

nums[0]

[1]

[2]

10

20

30

3000

3004

3008

3000

int *array

array

This line prints the element that is zero items

from the top of where ‘array’ points to.

This line prints the element that is one item from the top.

Since array starts at 3000, and each item is an integer, our next integer is at 3004…

10

20

Here we’re passing the address of the second element of the array.

Since nums[0] is at address 3000, nums[1] one is 4 bytes down at

3004.3004

The array parameter variable is actually a

pointer!

You can use [ ] syntax if you like but it’s REALLY a

pointer!

Page 22: Lecture #3

22

array

Pointer Arithmetic and Arrays

void printData(int array[ ]){ cout << array[0] << “\n”; cout << array[1] << “\n”;}int main(){ int nums[3] = {10,20,30};

printData(nums);

printData(&nums[1]);

printData(nums+1);}

Did you know that when you pass an array to a function…

You’re really just passing the address to the start of the

array!… not the array itself!

nums[0]

[1]

[2]

10

20

30

3000

3004

3008

3004

array

Our printData function thinks the array actually starts at location 3004!

It knows nothing about the earlier part of the array!

This prints the element that’s zero items from the top of where our array

starts…

i.e., the item at location 3004!

1020

[0]

[1]

20

and this prints the item that is one item down from the start of the array…

If the array starts at 3004, then the next integer is in slot 3008!

30

3000

When you use recursion on arrays,

you’ll often use this notation…

To process successively smaller suffixes of the array.

This line is tricky!

First, what happens when you just write the name of an array all by

itself?

Answer: C++ replaces the name with the start address of the array.

Ok. But what happens when we add 1 to the address? Do we get a value of 3001?

Let’s say you’re at 3000 Gayley and you ask “What’s the address of the next

building?”

Well, it’s probably not 3001. It’s probably something like 3010, right?

Similarly, if you’re at address 3000 and you ask for the address of the array

element that’s one slot down, the next element isn’t at 3001… in this case, it’s

at 3004!

+ one array element down

So this statement:

nums + 1

is really saying

“Advance one element (one integer) down

from the start of the nums array.”

3004

Page 23: Lecture #3

23

Pointers Work with Structures Too!

struct Nerd{int numZits;int hoursOfStarCraft;

};

int main(){ Nerd carey; Nerd *ptr;

ptr = &carey;

(*ptr).numZits = 140;

}

000010000000100100001002000010030000100400001005000010060000100700001008000010090000101000001011000010120000101300001014000010150000101600001017

carey

.numZits

.hoursOfStarCraft

140

42

1000ptr

Or you can use C++’s -> operator to access fields!

ptr->hoursOfStarCraft = 42;

You can use pointers to access structs too! Use the * to get to the structure,

and the dot to access its fields.

Page 24: Lecture #3

2530003001300230033004300530063007300830093010

class Circ{public: Circ(float x, float y, float rad) { m_x = x; m_y = y; m_rad = rad; }

float getArea(void) { return (3.14 * m_rad * m_rad); }

private: float m_x, m_y, m_rad;};

void printInfo(Circ *ptr){ cout << “The area is: “; cout << ptr->getArea();}

int main(){ Circ foo(3,4,10);

printInfo(&foo);}

Classes and Pointers

You can use pointers with classes just like you do with

structs.

class Circ{public: Circ(float x, float y, float rad) { m_x = x; m_y = y; m_rad = rad; }

float getArea(void) { return (3.14 * m_rad * m_rad); }

private: m_x m_y m_rad};

foo

3 4 10

3000

3 4 10

ptr 3000

The area is:

C++: Since ptr points to 3000, I’ll call the

getArea function associated with the

foo variable at 3000!

1010

314

(*ptr).getArea();

You can also use this alternate syntax…

(It does the same thing)

Page 25: Lecture #3

26

And now it’s time for your favorite game!

Page 26: Lecture #3

27

A New Type of VariableThus far, all variables we’ve defined have either been

local variables,

void foo(void){ int a; cin >> a;}

int aGlobalVariable;

int main(){ Circ a(3,4,10); float c[10];

c[0] = a.getArea();}

or class member variables.

class Student{public: string getZits(void) { int numZits = m_age * 5; return(numZits); }private: string m_name; int m_age;};

global variables

Let’s learn about a new type of variable: a dynamic variable

Page 27: Lecture #3

28

Dynamic VariablesYou can think of traditional variables like rooms in your

house.Just like a room can hold a person, a variable holds a

value.

But what if you run out of rooms because all of your aunts and uncles surprise

you and come over.

In this case, you have to call a hotel, reserve some rooms, and place your relatives in the hotel

rooms instead.

?

?

Page 28: Lecture #3

29

Dynamic VariablesIn a similar fashion, sometimes

you won’t know how many variables you’ll need until your

program runs.In this case, you can

dynamically ask the operating system to reserve new memory

for variables.The operating system will allocate room for

your variable in the computer’s free memory and then return the address of the new

variable.When you’re done with the variable, you can tell

the operating system to free the space it occupies for someone else to use.

Page 29: Lecture #3

30

New and DeleteFor example, let’s say we want to define an array, but we won’t know how big to make it until our program actually

runs …

int main(void){ int *arr; int size; cin >> size;

arr = new int[size];

arr[0] = 10; arr[2] = 75;

delete [] arr;}

The new command can be used to allocate an arbitrary amount of memory

for an array. How do you use it?

1. First, define a new pointer variable.2. Then determine the size of the array you need.

3. Then use the new command to reserve the memory. Your pointer gets the address of the memory.

4. Now just use the pointer just like it’s an array!

5. Free the memory when you’re done (check your relatives out of the hotel).

#1

#2

#3

#4

#5

Note: Don’t forget to include brackets

delete [ ] ptr;

if you’re deleting an array…

Your pointer variable will then be assigned the

address of the new array.

So it points to the new array!

Page 30: Lecture #3

31

New and Delete

int main(void){ int size, *arr; cout << “how big? ”; cin >> size;

arr = new int[size];

arr[0] = 10; // etc

delete [] arr;}

The new command requires two pieces of information:

1. What type of array you want to allocate.

2. How many slots you want

in your array.Make sure that the pointer’s

typeis the same as the type of

array you’re creating!

Page 31: Lecture #3

32

int main(void){ int *arr; int size;

cin >> size;

arr = new int[size];

arr[0] = 10; // etc

delete [] arr;}

New and Delete…

000010000000100100001002000010030000100400001005000010060000100700001008

arr

size

3

3

First, the new command determines how much memory it needs for the

array.

4 * 3 = 12 bytes

Page 32: Lecture #3

33

int main(void){ int *arr; int size;

cin >> size;

arr = new int[size];

arr[0] = 10; // etc

delete [] arr;}

New and Delete…

000010000000100100001002000010030000100400001005000010060000100700001008

arr

size

3

Next, the new command asks the operating system to reserve that

many bytes of memory.

4 * 3 = 12 bytes

Operating System – can you reserve 12 bytes of

memory for me?

...

0003005000030051000300520003005300030054

...00030060

Ok.. I found 12 bytes of free memory at address

30050.

Page 33: Lecture #3

34

int main(void){ int *arr; int size;

cin >> size;

arr = new int[size];

arr[0] = 10; // etc

delete [] arr;}

…000010000000100100001002000010030000100400001005000010060000100700001008

arr

size

3

Finally, your pointer variable gets the address of the newly reserved memory.

4 * 3 = 12 bytes

...

0003005000030051000300520003005300030054

...00030060

30050

10

New and Delete

You can also use the * notation if you like (instead of brackets)

*(arr+0) = 10; // arr[0] = 10;*(arr+1) = 20; // arr[1] = 20;

You can now treat your pointer just like an array!

(i.e. use [ ] to index it)

Page 34: Lecture #3

35

int main(void){ int *arr; int size;

cin >> size;

arr = new int[size];

arr[0] = 10; // etc

delete [] arr;}

…000010000000100100001002000010030000100400001005000010060000100700001008

arr

size

3

...

0003005000030051000300520003005300030054

...00030060

30050

10When you’re done, you use the

delete command to free the array.

Usage: delete [] ptrname;

Ok.. I’ll let someone else use that memory now…

New and Delete

arr[0] = 50; }

… not the pointer variable itself!

Our pointer variable still holds the address of the

previously-reserved memory slots!

Note: When you use the delete command, you

free the pointed-to memory…

But they’re no longer reserved for this program!

So don’t try to access them or bad things will

happen!

Operating System – I’m done with my 12 bytes of

memory at location 30050.

CRASH!

Page 35: Lecture #3

36

int main(void){ double *arr; int size;

cin >> size;

arr = new double[size];

// succesfull allocation arr[5] = 12345; arr[7] = 61616; delete [] arr;}

…000010000000100100001002000010030000100400001005000010060000100700001008

arr

size 4000000000

4000000000

If the new command fails (i.e. there’s not enough memory), then the new command will

cause your program to CRASH!

8 * 4000000000= 32 billion bytes

Operating System – can I have… uh… err..

32 BILLION bytes of memory?

Ok – well in that case, I’ll just CRASH the

program… HA!

32 BILLION?!!?? ARE YOU SMOKING CRACK? NO WAY!

New and Delete

CRASH!

C++ has a way for the programmer to check for such

errors and address them properly…

But that’s beyond the scope of CS32(So for now, don’t worry about checking for errors in this case)

Page 36: Lecture #3

37

class MathProf{public: MathProf() { for (int j=0;j< 100 ;j++) m_arr[j] = j*j;

 

void printSquares() { for (int j=0;j< 100 ;j++) cout << m_arr[j];

}private:

int m_arr[100];};

Using new and delete in a class

Well, here we have a math professor class…

int main(void){ MathProf stupidProf(5); MathProf smartProf(100);

stupidProf.printSquares(); smartProf.printSquares(); }

So how we might use new/delete within a class?

And as you can see, math profs can only memorize up to 100

square #s!Let’s update our class so they can memorize as many #s as

they like!

MathProf(int n) {

Step #1:Change our fixed array to a pointer variable and add a size variable.

Step #2:Update our constructor so the user can pass in the size of the prof’s array.

Step #3:Use the new command to allocate an array of the right size. Remember its size!

m_n

Step #5:Update our loop so we print out all N numbers.

~MathProf() { delete [] m_arr; // free memory}

int *m_arr, m_n;

m_n

m_arr = new int[n]; // alloc arraym_n = n; // store its size!

Step #4:Update our loop so we compute the first n square numbers (instead of just the first 100)

Step #6:Add a destructor that frees the dynamic array when we’re done!

Page 37: Lecture #3

38

More New and Delete So we just saw how to use new and delete to allocate arrays…

We can also use new and delete to allocate non-array variables.

Let’s see!

Page 38: Lecture #3

39struct Book{ string title; string author;};

class CompSciStudent{public: CompSciStudent(string name) { m_myBook = nullptr; m_myName = name; }

void giveBook(string t, string a) { m_myBook = new Book; m_myBook->title = t; m_myBook->author = a; }

~CompSciStudent() { delete m_myBook; }private: Book *m_myBook; string m_myName;};

As we know, most Comp Sci students

hate to carry around heavy books unless

they absolutely have to.

So if I just define a CS student he won’t

by default have a book… (Nor will he have to reserve thememory required to

hold a book)

int main(void)

{

CompSciStudent s(“Hal”);

...

}

s m_myBook

m_myName

nullptr

“Hal”Operating System, can

you free the memory for me at location nullptr?

OS: There’s no need to. The user never allocated any

memory (I can tell by the nullptr value)

“Hal”

nullptr is a special constant used to indicate an invalid or unused

pointer. This indicates that the m_myBook variable doesn’t hold a valid

address at this time.

Page 39: Lecture #3

40struct Book{ string title; string author;};

class CompSciStudent{public: CompSciStudent(string name) { m_myBook = nullptr; m_myName = name; }

void giveBook(string t, string a) { m_myBook = new Book; m_myBook->title = t; m_myBook->author = a; }

~CompSciStudent() { delete m_myBook; }private: Book *m_myBook; string m_myName;};

But what if we have a particularly nerdy

CS student and we’ve given her a book to

hold. Let’s seewhat happens!

int main(void)

{

CompSciStudent s(“Liz”);

s.giveBook("Calc", "Bill Nye");

}

s m_myBook

m_myName

nullptr

“Liz”

OS: Sure thing. Here’s some memory for you at address

45000.

45000

44999 ...

45100 ...

45000

title

author

"Calc"

"Bill Nye"

OS: Sure thing. I’ll free it for someone else to use.

So now you see how we can use dynamic variables to ensure

that we only allocate the minimum amount of memory that our

classes need!

“Liz”

"Calc" "Bill Nye"

Operating System, can you free the memory for me at location 45000?

Operating System, can you allocate enough memory to

hold a Book structure for me?Finally, notice that we don’t

need the [ ] brackets when we delete m_myBook!

This is because we allocated just one book…

not a whole array of them!

Page 40: Lecture #3

41 Using new and delete to Allocate Class Variables

So we saw how to use new and delete to allocate a struct

variable. Can we use new and delete to

allocate a class variable which has constructors and/or destructors?Yes!

Let’s see how it works.

Page 41: Lecture #3

42

0000400000004002000040040000400600004008

…...

int main(void){ Waldo *ptr; ptr = new Waldo(165);

...

delete ptr;}

000010000000100200001004000010060000100800001010

...

ptrclass Waldo{public: Waldo(int weight) { m_weight = weight; m_bacteria = 0; }

~Waldo() { m_weight = 0; // DEAD! m_bacteria = 1000000; //eww }

private: int m_weight, m_bacteria;};

new/delete With C’tors, D’tors

Part #1: C++ reserves memory for your object

”Hey Operating System, can you reserve enough bytes to hold a Waldo variable for me?”

Sure. I just reserved some memory for you

at address 4000.

Now lets see how new and delete work with classes

containing constructors and destructors!

4000

When you use the new command to allocate a class

with a constructor, C++ uses a two-part process!

.m_weight

.m_bacteria

Page 42: Lecture #3

43

Now lets see how new and delete work with classes

containing constructors and destructors!

0000400000004002000040040000400600004008

...

ptr 000010000000100200001004000010060000100800001010

...

int main(void){ Waldo *ptr; ptr = new Waldo(165);

...

delete ptr;}

new/delete With C’tors, D’tors

...class Waldo{public: Waldo(int weight) { m_weight = weight; m_bacteria = 0; }

~Waldo() { …

Part #2: C++ calls the class’s constructor to initialize the newly allocated memory

“Now that I’ve allocated enough memory to hold Waldo, I’ll call his constructor to initialize

him!”

165

4000

.m_weight

.m_bacteria165

0

Page 43: Lecture #3

44

When you use the delete command to free an object with a destructor, C++ also uses a

two-part process!

int main(void){ Waldo *ptr; ptr = new Waldo(165);

...

delete ptr;}

new/delete With C’tors, D’tors

class Waldo{public: ...

~Waldo() { m_weight = 0; m_bacteria = 1000000; }

0000400000004002000040040000400600004008

ptr 000010000000100200001004000010060000100800001010

...

4000

.m_weight

.m_bacteria165

0OK. I’ll free up that

memory for someone else…

Part #1: C++ calls the class’s destructor

“While I still have ownership of Waldo’s memory, I’m going to call

Waldo’s destructor on it.”

Part #2: C++ asks the Operating System to free the memory

“Hey O.S., now that I’ve run Waldo’s destructor, can you free that memory at address 4000 for

me.”

0 1000000

Page 44: Lecture #3

45

Using new and delete to Allocate Class Instances

When we use new to allocate a class instance, this is what happens:

1. Memory is allocated by the OS for us.

2. The constructor for the class is called on this memory, to initialize it (if the class has a c’tor).

When you delete a class instance, this is what happens:

1. The destructor for the class is called, first (if the class has a destructor).

2. The memory is released to the OS.

Page 45: Lecture #3

Classes and the “this” Pointer

Before C++, in the dark ages when Carey learned programming, we didn’t use classes!

Let’s see how we used to do things… with structs, pointers, and functions instead of classes!

And maybe this will help us understand how C++ classes actually

work!

Page 46: Lecture #3

The Old Days…Before Classes

Before C++, we would use structs,

pointers and regular functions

to create class-like programs.

struct Wallet{ int num1s, num5s;};

void Init(Wallet *ptr){ ptr->num1s = 0; ptr->num5s = 0;}

void AddBill(Wallet *ptr, int amt){ if (amt == 1) ptr->num1s++; else if (amt == 5) ptr->num5s++;}

void main(void){ Wallet w; Init(&w);

AddBill(&w , 5);}

4000

4000

wnum1s

num5s

ptr 4000

00

ptr 4000

4000

To let a function operate on a struct variable, we used to have to pass in the variable’s address...

The function can then use its pointer to get

to the original variable.

As it turns out, C++ classes work in an almost identical

fashion!

5

amt 5

A Wallet structure keeps track of how

many $1 and $5 the wallet holds…The Init() function

initializes the wallet...

It’s like a constructor.

And the AddBill() function lets us add a bill to the

wallet...

by updating the contents of the struct.

1

Page 47: Lecture #3

class Wallet{public: void Init(); void AddBill(int amt); …

private: int num1s, num5s; };

void Wallet::Init() { num1s = num5s = 0; }void Wallet::AddBill(int amt) { if (amt == 1) num1s++; else if (amt == 5)num5s++;}

The Wallet ClassHere’s a class equivalent of

our old-skool Wallet…

As you can see, we can initialize a new wallet…

And we can add either a $1 or $5 bill to our wallet.

Our wallet then keeps track of how many bills of

each type it holds…

int main(){ Wallet a;

a.Init(); a.AddBill(5); }

And here’s how we might use our class…

Page 48: Lecture #3

Every time you call a member function of an object, e.g.:

C++ invisibly rewrites your function call and passes in the variable’s address!

a.addBill(5);

addBill(&a, 5);

And C++ does the same thing to your actual member functions!

int main(){ Wallet a, b;

a.Init(); b.AddBill(5);}

void Wallet::Init() { num1s = num5s = 0; }void Wallet::AddBill(int amt) { if (amt == 1) num1s++; else if (amt == 5)num5s++;}...

int main(){ Wallet a, b;

Init( AddBill( }

);a. & 5);b. & ,

void Init( { num1s = num5s = 0; }void { if (amt == 1) num1s++; else if (amt == 5) num5s++;}...

Wallet )

Wallet int amt)

::

::

*thisInit(Wallet *this)

this-> this->

AddBill(*this,

this->this->

So here we’re calling the Init() method of a… But here’s what’s

REALLY happening!

It adds a hidden first argument that’s a pointer to your original variable!

Here what your Init() method looks like… But here’s what’s

REALLY happening!

Classes and the “this” Pointer

Page 49: Lecture #3

int main(){ Wallet a, b;

a.Init(); a.AddBill(5);}

void Wallet::Init() { num1s = num5s = 0; }void Wallet::AddBill(int amt) { if (amt == 1) num1s++; else if (amt == 5)num5s++;}...

int main(){ Wallet a, b;

Init(&a); AddBill(&b,5);}

void Init(Wallet *this) { this->num1s = this->num5s = 0; }void AddBill(Wallet *this, int amt) { if (amt == 1) this->num1s++; else if (amt==5) this->num5s++;}...

C++ converts all of your member functions automatically and invisibly by adding an extra pointer parameter called “this”:

Yes… the pointer is actually called “this”!

Classes and the “this” Pointer

Page 50: Lecture #3

int main(){ Wallet a;

a.Init(&a);

a.AddBill(&a , 5); }

void Wallet::Init(Wallet *this) { this-> num1s = this-> num5s = 0; }void Wallet::AddBill(Wallet *this, int amt) { if (amt == 1) this-> num1s++; else if (amt==5) this-> num5s++;}...

1000

a num1s

num5s

1000

this 1000

0

1000

01

This is how it actually works under the hood….

But C++ hides the “this pointer” from you to simplify things.

Classes and the “this” Pointer

5

amt 5

Page 51: Lecture #3

int main(){ Wallet a;

a.Init();

}

void Wallet::Init() { num1s = num5s = 0;

}void Wallet::AddBill(int amt) { if (amt == 1) num1s++; else if (amt == 5) num5s++;}...

Your class’s methods can use the this variable to determine

their address in memory!

While C++ hides the “this pointer” from you, if you want, your class’s methods can explicitly use

it.

cout << “I am at address: “ << this;

cout << “a is at address: “ << &a;

a num1s

num5s

100000

I am at address: 1000a is at address: 1000

Classes and the “this” Pointer

this->num1s = this->num5s = 0;

You can explicitly use the “this” variable in your methods if you

like!

It works fine!

So now you know how C++ classes work under the

hood!

Page 52: Lecture #3

53

Copy Construction

Last time we saw how to create a

constructor function for a class…

class Circ{public:

float GetArea(void) const;private:float m_x, m_y, m_rad;

};

Circ(int x, int y, int r) { m_x = x; m_y = y; m_rad = r; }

Our simple constructor accepts

three ints as arguments..

Question: Can constructors accept other types of

variables as parameters?Let’s see…

Page 53: Lecture #3

54

class Circ{public:

float GetArea(void) const;private:float m_x, m_y, m_rad;

};

Circ(int x, int y, int r) { m_x = x; m_y = y; m_rad = r; }

Copy Construction

For example, what if I have

a Point class like this…

class Circ{public:

float GetArea(void) const;private:float m_x, m_y, m_rad;

};

Circ( ) {

}

If we like, we can define a Circ constructor that

accepts a Point variable as an argument!

class Point // an x,y coordinate{public: int m_x, m_y; };

const Point &ptAnd of course, we still want

our constructor to have a radius parameter…

, int rad

m_x = pt.m_x;m_y = pt.m_y;m_rad = rad;

Finally, we can write our constructor’s body…

Allright, let’s see it in action…

const means that our function

can’t modify the pt variable.

The & means “pass by

reference” which is more efficient.

Page 54: Lecture #3

55

class Circ{public:

float GetArea(void) const;private:float m_x, m_y, m_rad;

};

Circ(int x, int y, int r) { m_x = x; m_y = y; m_rad = r; }

Copy Construction

class Circ{public:

float GetArea(void) const;private:float m_x, m_y, m_rad;

};

Circ( ) {

}

class Point // an x,y coordinate{public: int m_x, m_y; };

const Point &pt, int rad

m_x = pt.m_x;m_y = pt.m_y;m_rad = rad;

int main(){ Point p; p.m_x = 7; p.m_y = 9;

Circle c(p,3);

cout << c.getArea(); ...}

p m_xm_y

79

class Circ{ ... Circ(const Point &pt,int rad) { m_x = pt.m_x; m_y = pt.m_y; m_rad = rad; } ...private: m_x m_y m_rad }

c

3

79

3

And now we have a fully constructed

Circle!

Page 55: Lecture #3

56

Copy Constructionclass Circ{public:

float GetArea(void) const;private:float m_x, m_y, m_rad;

};

Circ( ) {

}

const Point &pt, int rad

m_x = pt.m_x;m_y = pt.m_y;m_rad = rad;

Circ(int x, int y, int r) { m_x = x; m_y = y; m_rad = r; }

Ok, so we’ve seen a simple

constructor… And a constructor

that accepts another class’s

variable… What if we want to define a

constructor for Circ that accepts another Circ variable??

That makes

my head spin!

int main(){ Circ a(1,2,3);

Circ b(a); ...}

This will allow us to initialize a new Circ

variable (b) based on the value of an existing

Circ variable (a).Let’s see how to do it! Circ( )

{

}

const Circ &old

m_x = old.m_x;m_y = old.m_y;m_rad = old.m_rad;

Page 56: Lecture #3

57

Copy Construction

int main(){ Circ a(1,2,3);

Circ b(a); ...}

class Circ{ ... Circ(int x, int y, int rad) { m_x = x; m_y = y; m_rad = rad; } ...private: m_x m_y m_rad }

a

321

1 2 3

class Circ{public:

float GetArea(void) const;private:float m_x, m_y, m_rad;

};

Circ(int x, int y, int r) { m_x = x; m_y = y; m_rad = r; }

Circ( ) {

}

const Circ &old

m_x = old.m_x;m_y = old.m_y;m_rad = old.m_rad;

Circ( ) {

}

const Point &pt, int rad

m_x = pt.m_x;m_y = pt.m_y;m_rad = rad;

class Circ{ ... Circ( const Circ &old ) { m_x = old.m_x; m_y = old.m_y; m_rad = old.m_rad; } ...private: m_x m_y m_rad }

b

But wait! Circ variable b is accessing the private

variables/functions of Circ variable a – isn’t that violating C++ privacy

rules?

1 2 3This means:“Initialize variable b

based on the value of

variable a.”

This kind of thing is actually pretty useful… It lets us

create a new variable with the same value as an

existing variable.

Carey says:

That’s not a problem. Every Circ variable is allowed to “touch” every

other Circ variable’s privates – “private” protects one class from

another, not one variable from another (of the same class)!

So every CSNerd object can touch every other CSNerd object’s

privates.

But a CSNerd can’t touch an EENerd’s privates (for obvious

reasons).

Page 57: Lecture #3

58

Copy Construction

int main(){ Circ a(1,2,3);

Circ b(a); ...}

class Circ{public:

float GetArea(void) const;private:float m_x, m_y, m_rad;

};

Circ(int x, int y, int r) { m_x = x; m_y = y; m_rad = r; }

Circ( ) {

}

const Circ &old

m_x = old.m_x;m_y = old.m_y;m_rad = old.m_rad;

Circ( ) {

}

const Point &pt, int rad

m_x = pt.m_x;m_y = pt.m_y;m_rad = rad;

In C++ talk, this function is called a “copy

constructor.”A copy constructor is a constructor function

that is used toinitialize a new variable from an existing variable

of the same type.

Page 58: Lecture #3

59

Copy Constructionclass Circ{public:Circ(float x, float y, float r){

m_x = x; m_y = y; m_rad = r;}

float GetArea(void){ return(3.14159*m_rad*m_rad);}

private:float m_x, m_y, m_rad;

}; 

Circ( const Circ & oldVar){ m_x = oldVar.m_x; m_y = oldVar.m_y; m_rad = oldVar.m_rad;}

int main(){ Circ a(1,1,5);

Circ b(a);

cout << b.GetArea();}

A Copy Constructor is just like a regular

constructor.

However, it takes another instance of the

same class as a parameter instead of

regular values.

Page 59: Lecture #3

60

Copy Constructionclass Circ{public:Circ(float x, float y, float r){

m_x = x; m_y = y; m_rad = r;}

float GetArea(void){ return(3.14159*m_rad*m_rad);}

private:float m_x, m_y, m_rad;

}; 

Circ( const Circ & oldVar){

m_x = oldVar.m_x; m_y = oldVar.m_y; m_rad = oldVar.m_rad;}

The parameter to your copy

constructor should be const!The parameter to

your copy constructor

must be a reference!The type of your

parameter must be the same type as the

class itself!

This is a promise that you won’t modify the

oldVar while constructing your new variable!

oldVar.m_x = 10; // error ‘cause of const

This one’s a bit more difficult to explain right

now.

For now, just make sure you use an & here!

Page 60: Lecture #3

61

Copy Constructionclass Circ{public:Circ(float x, float y, float r){

m_x = x; m_y = y; m_rad = r;}

float GetArea(void){ return(3.14159*m_rad*m_rad);}

private:float m_x, m_y, m_rad;

}; 

Circ( const Circ & oldVar){ m_x = oldVar.m_x; m_y = oldVar.m_y; m_rad = oldVar.m_rad;}

Oh, C++ also allows you to use a simpler syntax…

Instead of writing:

Circ b(a);

which is ugly…

You can write:

Circ b = a;

It does exactly the same thing! It defines a new

variable b and then calls the copy constructor!

int main(){ Circ a(1,2,3);

Circ b(a);} Circ b = a; // same!

Page 61: Lecture #3

62

Copy Constructionclass Circ{public:Circ(float x, float y, float r){

m_x = x; m_y = y; m_rad = r;}

float GetArea(void){ return(3.14159*m_rad*m_rad);}

private:float m_x, m_y, m_rad;

}; 

Circ( const Circ & oldVar){ m_x = oldVar.m_x; m_y = oldVar.m_y; m_rad = oldVar.m_rad;}

The copy constructor is not just used when you initialize a new variable from an existing one:

Circ b(a);

It’s used any time you make a new copy of an existing class variable.

Can anyone think of other times when a copy

constructor would be used?

Page 62: Lecture #3

63

Copy Constructionvoid foo(Circ temp){ cout << “Area is: “ << temp.GetArea();}

int main(){ Circ a(1,2,10);

foo(a);}

class Circ{public:Circ(float x, float y, float r){

m_x = x; m_y = y; m_rad = r;}

float GetArea(void){ return(3.14159*m_rad*m_rad);}

private:float m_x, m_y, m_rad;

}; 

Circ( const Circ & oldVar){ m_x = oldVar.m_x; m_y = oldVar.m_y; m_rad = oldVar.m_rad;}

Here’s a simple program that passes a circle to a

function…

Any guesses if/when the copy constructor is

called?

class Circ{ ... Circ(int x, int y, int rad) { m_x = x; m_y = y; m_rad = rad; } ...private: m_x m_y m_rad }

a

1 2 10

Wait a second!

We’re creating a new variable called temp and copying the value of a into it! Hmmm!!!

class Circ{ ... Circ(const Circ &old) { m_x = old.m_x; m_y = old.m_y; m_rad = old.m_rad; } ...private: m_x m_y m_rad }

temp

1 2 10

Now our temp variable has been copy-constructed, it can be used normally by our foo

function!

Page 63: Lecture #3

64

Copy Constructionclass Circ{public:Circ(float x, float y, float r){

m_x = x; m_y = y; m_rad = r;}

float GetArea(void){ return(3.14159*m_rad*m_rad);}

private:float m_x, m_y, m_rad;

}; 

Circ(const Circ & oldVar){ m_x = oldVar.m_x; m_y = oldVar.m_y; m_rad = oldVar.m_rad;}

If you don’t define your own copy constructor…

C++ will provide a default one for you…

It just copies all of the member variables

from the old instance to the new instance…

int main(){ Circ a(1,2,3);

Circ b(a);}

a m_xm_y

m_rad

123

b m_xm_y

m_rad

123

But then why would I ever need to define my own copy constructor?

Carey says:

“Patience, grasshopper! I’ll show

you in a minute!”

Page 64: Lecture #3

65

Copy Construction

Well, we’ll see very soon.

But first, I want to show you a new class called

Squares…

Ok – so why would we ever need to write our own Copy Constructor function?

After all, C++ copies all of the member variables for us automatically if we don’t

write our own!

Page 65: Lecture #3

66

Copy ConstructionLet’s consider another class for a second…

class Squares{public: Squares(int n) { m_n = n; for (int j=0;j<n;j++) m_sq[j] = (j+1)*(j+1);

~Squares() { } 

void printSquares() { for (int j=0;j<m_n;j++) cout << m_sq[j] << endl;

}private:

int m_sq[5], m_n;};

The Squares class can be used to compute the first n square numbers.

When you construct it, it fills in the array with each square number.

You can then call the printSquares function to print out the numbers.

Page 66: Lecture #3

67

Copy ConstructionLet’s consider another class for a second…

class Squares{public: Squares(int n) { m_n = n; for (int j=0;j<n;j++) m_sq[j] = (j+1)*(j+1);

~Squares() { } 

void printSquares() { for (int j=0;j<m_n;j++) cout << m_sq[j] << endl;

}private:

int m_sq[5], m_n;};

int main(){ Squares a(3); if (...) { Squares b = a; ... }

a.printSquares();  }

a m_n

m_sq

3

31 4 9

In this example, we initialize b to be the same

as a.

Page 67: Lecture #3

68

int main(){ Squares a(3); if (...) { Squares b = a; ... }

a.printSquares();  }

Copy Construction

class Squares{public: Squares(int n) { m_n = n; for (int j=0;j<n;j++) m_sq[j] = (j+1)*(j+1);

~Squares() { } 

void printSquares() { for (int j=0;j<m_n;j++) cout << m_sq[j] << endl;

}private:

int m_sq[5], m_n;};

When you init b from a, C++ copies the value of every member

variable from variable a to variable b.

b m_n

m_sq

31 4

a m_n

m_sq

31 4 9

This is called a “shallow copy.” a and b now contain identical

member values.

9

Let’s consider another class for a second…

Page 68: Lecture #3

69

Copy Construction

class Squares{public: Squares(int n) { m_n = n; for (int j=0;j<n;j++) m_sq[j] = (j+1)*(j+1);

~Squares() { } 

void printSquares() { for (int j=0;j<m_n;j++) cout << m_sq[j] << endl;

}private:

int m_sq[5], m_n;};

This is exactly what you’d want to happen, right?

a m_n

m_sq

31 4

b m_n

m_sq

31 4 9

1 4 9

int main(){ Squares a(3); if (...) { Squares b = a; ... }

a.printSquares();  }

// b’s d’tor called

149

Let’s consider another class for a second…

Page 69: Lecture #3

70

class Squares{public: Squares(int n) { m_n = n; m_sq = new int[n]; for (int j=0;j<n;j++) m_sq[j] = (j+1)*(j+1);

~ Squares(){delete []m_sq;} 

void printSquares() { for (int j=0;j<m_n;j++) cout << m_sq[j] << endl;

}private:

int *m_sq, m_n;};

Copy Construction

Now let’s look at an updated version of our

Squares class.

It uses new and delete to dynamically allocate memory for its array.

When constructed, it uses new to dynamically

allocate an array to hold its square numbers.

And when it is destructed, it uses

delete to release this array.

Let’s see what happens with this class.

Page 70: Lecture #3

71

Copy Construction

class Squares{public: Squares(int n) { m_n = n; m_sq = new int[n]; for (int j=0;j<n;j++) m_sq[j] = (j+1)*(j+1);

~ Squares(){delete []m_sq;} 

void printSquares() { for (int j=0;j<m_n;j++) cout << m_sq[j] << endl;

}private:

int *m_sq, m_n;};

int main(){ Squares a(3); if (...) { Squares b = a; ... }

a.printSquares();  }

3

a m_n

m_sq

3

Operating system, can you reserve 12 bytes of memory for

me?

000008000000080400000808Sure, I’ll reserve

12 bytes for you at address 800.

800

149

Page 71: Lecture #3

72

Copy Construction

class Squares{public: Squares(int n) { m_n = n; m_sq = new int[n]; for (int j=0;j<n;j++) m_sq[j] = (j+1)*(j+1);

~ Squares(){delete []m_sq;} 

void printSquares() { for (int j=0;j<m_n;j++) cout << m_sq[j] << endl;

}private:

int *m_sq, m_n;};

int main(){ Squares a(3); if (...) { Squares b = a; ... }

a.printSquares();  }

a m_n

m_sq

3000008000000080400000808800

149

b m_n

m_sq

3

800

Now, watch what happens when we create our new b variable and shallow copy a’s member variables into

it…

Both a’s m_sq pointer…

And b’s m_sq pointer…

Point to a’s original copy of the array!

Page 72: Lecture #3

73

Copy Construction

class Squares{public: Squares(int n) { m_n = n; m_sq = new int[n]; for (int j=0;j<n;j++) m_sq[j] = (j+1)*(j+1);

~ Squares(){delete []m_sq;} 

void printSquares() { for (int j=0;j<m_n;j++) cout << m_sq[j] << endl;

}private:

int *m_sq, m_n;};

int main(){ Squares a(3); if (...) { Squares b = a; ... }

a.printSquares();  }

a m_n

m_sq

3000008000000080400000808800

149

b m_n

m_sq

3

800

// b’s d’tor called

But that’s a problem!

Because when b is destructed…

It ends up deleting the array that’s really

owned by variable a!

Operating system, I’m done with the

memory at address 800.

Page 73: Lecture #3

74

Copy Construction

class Squares{public: Squares(int n) { m_n = n; m_sq = new int[n]; for (int j=0;j<n;j++) m_sq[j] = (j+1)*(j+1);

~ Squares(){delete []m_sq;} 

void printSquares() { for (int j=0;j<m_n;j++) cout << m_sq[j] << endl;

}private:

int *m_sq, m_n;};

int main(){ Squares a(3); if (...) { Squares b = a; ... }

a.printSquares();  }

// b’s d’tor called

???

Because now, when we try to access a’s array,

we get garbage!!!

a m_n

m_sq

3

800

That’s a big problem!

Page 74: Lecture #3

75

Copy Construction

class Squares{public: Squares(int n) { m_n = n; m_sq = new int[n]; for (int j=0;j<n;j++) m_sq[j] = (j+1)*(j+1);

~ Squares(){delete []m_sq;} 

void printSquares() { for (int j=0;j<m_n;j++) cout << m_sq[j] << endl;

}private:

int *m_sq, m_n;};

int main(){ Squares a(3); if (...) { Squares b = a; ... }

a.printSquares();  }

// b’s d’tor called

So what’s the moral of the story?

Any time your class holds pointer

member variables*…

And you make a copy of a class

instance…

BAD THINGS will happen when you

destruct either copy...

* or file objects (e.g., ifstream), network sockets, etc.

Page 75: Lecture #3

76

Copy Construction

So how do we fix this?

1. Determine how much memory is allocated by the old variable.

2. Allocate the same amount of memory in the new variable.

3. Copy the contents of the old variable to the new variable.

For such classes, you must define your own copy constructor!

a m_n

m_sq

3

800

000008000000080400000808

149

b m_n

m_sq

3

700

000007000000070400000708

149

Here’s how it works forSquares b = a;

The old variable requires three

slots of memory!

Page 76: Lecture #3

77

class Squares{public: Squares(int n) { … } ~ Squares(){ delete[]m_sq; }

// copy constructor  

void printSquares() { ... }

private:int *m_sq, m_n;

};

The Copy Constructorint main(){ Squares a(3); if (...) { Squares b = a; ... }

a.printSquares();  }

Let’s see how to define our copy constructor!

Squares(const Squares &src) { }

First our copy constructor must determine how much memory is required by the

new instance.

m_n = src.m_n;

This means: “The new instance must

have the same number of array slots as the old

instance.”

Page 77: Lecture #3

78

class Squares{public: Squares(int n) { … } ~ Squares(){ delete[]m_sq; }

// copy constructor  

void printSquares() { ... }

private:int *m_sq, m_n;

};

The Copy Constructorint main(){ Squares a(3); if (...) { Squares b = a; ... }

a.printSquares();  }

Let’s see how to define our copy constructor!

Squares(const Squares &src) { }

Next, our copy constructor must allocate its own copy of any dynamic memory!

m_n = src.m_n;

This ensures that the new instance has its own array and doesn’t share the old

instance’s array!

m_sq = new int[m_n];

Page 78: Lecture #3

79

class Squares{public: Squares(int n) { … } ~ Squares(){ delete[]m_sq; }

// copy constructor  

void printSquares() { ... }

private:int *m_sq, m_n;

};

The Copy Constructorint main(){ Squares a(3); if (...) { Squares b = a; ... }

a.printSquares();  }

Let’s see how to define our copy constructor!

Squares(const Squares &src) { }

Finally, we have to manually copy over the contents of

the original array to our new array.

m_n = src.m_n; m_sq = new int[m_n]; for (int j=0;j<m_n;j++) m_sq[j] = src.m_sq[j];

This ensures that the new instance has its own copy of

all of the data!

Page 79: Lecture #3

80

class Squares{public: Squares(int n) { … } ~ Squares(){ delete[]m_sq; }

// copy constructor Squares(const Squares &src) { m_n = src.m_n; m_sq = new int[m_n]; for (int j=0;j<m_n;j++) m_sq[j] = src.m_sq[j]; }  

void printSquares() { ... }

private:int *m_sq, m_n;

};

The Copy Constructorint main(){ Squares a(3); if (...) { Squares b = a; ... }

a.printSquares();  }

Let’s watch our correct copy constructor work!

a m_n

m_sq

3000008000000080400000808800

149

b m_n

m_sq

src

3

Operating system, the original variable needed 12 bytes of memory, so

can you reserve 12 bytes for me too?

Sure, I’ll reserve 12 bytes for you at address 900.

000009000000090400000908

900

Now I’ll copy the values from the old array into

my new array.

149

Page 80: Lecture #3

81

class Squares{public: Squares(int n) { … } ~ Squares(){ delete[]m_sq; }

// copy constructor Squares(const Squares &src) { m_n = src.m_n; m_sq = new int[m_n]; for (int j=0;j<m_n;j++) m_sq[j] = src.m_sq[j]; }  

void printSquares() { ... }

private:int *m_sq, m_n;

};

The Copy Constructorint main(){ Squares a(3); if (...) { Squares b = a; ... }

a.printSquares();  }

a m_n

m_sq

3000008000000080400000808800

149

b m_n

m_sq

3 000009000000090400000908

900

149

// b’s d’tor called

Operating System, can you free the memory at

address 900 for me.

No sweat, homie. Consider it freed.

We’re A-OK, since a still has its own array!

149