Uploaded by Salab Pazu

lecture03-updated

advertisement
1
Lecture #3
More pointers!
Copy Construction!
2
Arrays, Addresses and Pointers
int main() {
int nums[3] = {10,20,30};
cout <<
...
}
nums
[0]
[1]
[2]
When we execute
This
prints
C++ replaces this
nums;
cout << nums;
9242
during compilation
it prints "9242" – why?
with the address of
Our
array's
…
the array!Thinking time!
address
is
9242
00009240
Just like any other variable, every
9242
00009242
10 00009244
array has an address in memory.
00009246
But… in C++ you don’t use the
20 00009248
& operator to get an array’s address!
00009250
30 00009252
You simply write the array’s name
00009254
(without brackets) and C++ will
00009256
give you the array’s address!
00009258
00009260
3
Arrays, Addresses
Clearly thisand
givesPointers
us
the address of element
int the
main()
{ about
"Get
address
of
But
what
"and advance
2 offorward
the array.
= {10,20,30};
Andnums[3]
this
works
theint
nums
array..."
this?
by 2 element(s)."
int
*ptr
=
nums;
the same way!
What does this program print?
cout << &nums[2] << endl;
cout << nums + 2 << endl;
cout << ptr + 2 << endl;
}
…
nums
[0]
[1]
[2]
ptr
00009240
00009242
10 00009244
00009246
20 00009248
00009250
30 00009252
00009254
00009256
9242 00009258
00009260
Thinking
9250
9250
9250
time!
Assuming that an address/pointer p
points at the top of an array...
Adding j to p gives you the address of
the jth element in the array.
Adding an integer with an
address/pointer is called
"pointer arithmetic."
4
"Give
me the value
Arrays,
Addresses and Pointers
that's two slots down
int main()
{ the top of the
from
"Give me the value that's
int nums[3] = {10,20,30};
nums array." two slots down from
int *ptr = nums;
where ptr points
Whatat."
does this program print?
cout
cout
cout
cout
<<
<<
<<
<<
}
nums[2] << endl;
*(nums + 2) << endl;
*(ptr + 2) << endl;
ptr[2] << endl;
…
nums
[0]
[1]
[2]
ptr
00009240
00009242
10 00009244
00009246
20 00009248
00009250
30 00009252
00009254
00009256
9242 00009258
30 Thinking
30
30
30
time!
"Give me theWe
value
canthat's
use pointers just like
two slots they're
down from
arrays, and visa-versa!
where ptr points at."
In C++, the two syntaxes have
identical behavior:
ptr[j]
*(ptr + j)
arr[j]
*(arr + j)
5
PointerYouArithmetic
can use [ ] syntax and Arrays
The array
parameter variable
is actually a pointer!
if you like but it’s
REALLY a pointer!Did
array
void printData(int
int array[
*array])
{
}
cout << array[0] << “\n”;
cout << array[1] << “\n”;
you know that when you pass
an array
This prints the element
thatto a function…
is zero down from where the
array pointer
points.
You’re
really just passing the
address to the start of the array!
… not the array itself!
This prints the
element that is one
int main()
downpassing
from where
the
Here
we’re
the
{
array
pointer
points.
address
of
the
second
int nums[3] = {10,20,30};
element of the array.
}
array
nums
[0]
10
printData(nums);
[1]
20
printData(&nums[1]);
[2]
30
printData(nums+1);
3000
3004
3008
6
Pointer Arithmetic and Arrays
Now our printData
function thinks the array
starts at location 3004!
array 3004
void printData(int array[ ])
{
}
int nums[3] = {10,20,30};
printData(nums);
}
address to the start of the array!
cout << array[0] << “\n”;
cout << array[1] << “\n”;
int main()
{
Did you know that when you pass
This prints the element
This printsanthe
array to a function…
that’s zero items down
element that’s one
from where our pointer
item down from where
points at…
just passing the
our pointerYou’re
points really
at…
… not
array
itself!
Itthe
knows
nothing
about the earlier
part of the array!
nums
[0]
array
nums + 1 points here!
[0]
[1]
10
[2]
[1]
30
printData(&nums[1]);
+ one array element down
3004
printData(nums+1);
10
20
20
3000
3004
3008
7
3000
3001
3002
You can use pointers with
{ m_x = x; m_y = y; m_rad = rad; } 3003
classes and structs too!
3004
float getArea()
{ return 3.14 * m_rad * m_rad; }
3005
class Circ
…
3006
{
3007
private:
public:
m_x
m_y
m_rad
3008
3
4
10
};
Circ(float x, float y, float rad)
If we have a
3009
{ m_x = x; m_y = y; m_rad = rad; }pointer to a
3010
This calls the getArea()
This
alternate
struct or class...
float getArea()
function associated with the
syntax works …
the
ptr
Classes, Structs and Pointers foo
class Circ
{
public:
Circ(float x, float y, float rad)
{ return 3.14 *object
m_radat* address
m_rad; }3000!
same way!
void printInfo(Circ *ptr) {
…
cout << “The area is: “;
cout << ptr->getArea();
(*ptr).getArea();
private:
}
float m_x, m_y, m_rad;
};
The area is: 314
main()its
{
we canint
access
membersCirc
usingfoo(3,4,10);
the
-> operator.
3000
printInfo(&foo);
}
8
Meme, anyone?
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!
The Old Days…Before
Classes
A Wallet structure
keeps track of how
many $1 and $5 the
The
function
can
then
struct Wallet
walletfunction
holds…
The Init()
use its pointer to get to
{
initializes the wallet...
the
original
variable.
int num1s, num5s;
It’s like a constructor.
};
w
0 4000
Before C++, we
num5s 0
would use1structs,
num1s
pointers and regular
ptr 4000
functions
to create
ptr 4000
class-like
programs.
void Init(Wallet *ptr)
And the AddBill() function lets
{
amt
5
us add a bill to the
To wallet...
let a function
ptr->num1s = 0;
operate on a struct
contents of
ptr->num5s = 0; by updating thevariable,
we used to
the struct.
}
have to pass in the
void main()
variable’s address...
{
void AddBill(Wallet *ptr, int amt)
Wallet w;
{
if (amt == 1) ptr->num1s++;
4000
Init(&w);
else if (amt == 5) ptr->num5s++;
}
4000 5
AddBill(&w , 5);
}
As it turns out, C++ classes work in
an almost identical fashion!
The Wallet Class
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++;
}
And here’s how we might
use our class…
Here’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);
}
Classes and the “this” Pointer
Here
youraInit()
Every
timewhat
you call
member function of an object, e.g.:
But here’s
what’s REALLY
And C++ does
the
same
thing
to your actual member
functions!
method looks like…
a.addBill(5);
happening! 
It invisibly
adds a hidden
first
argument
that’s
a pointer
original variable!
C++
rewrites
your
function
call and
passestoinyour
the variable’s
address!
addBill(&a, 5);
void Wallet::Init()
{
num1s =
num5s = 0;
}
void Wallet::AddBill(int amt)
{
if (amt == 1)
num1s++;
else if (amt == 5)num5s++;
}
So here we’re calling the
...
Init() method of a…
int main()
{
Wallet a, b;
::Init( )
void WalletInit(Wallet
*this)
*this
{
this-> num1s = this-> num5s = 0;
}
void Wallet :: AddBill(
int amt)
*this,
{
if (amt == 1)
this-> num1s++;
else if (amt == 5) this-> num5s++;
}
But here’s what’s REALLY
...
happening! 
int main()
{
Wallet a, b;
a.Init(&
);
b.AddBill(&5);
,
a.Init();
b.AddBill(5);
}
}
Classes and the “this” Pointer
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”!
void Wallet::Init()
{
num1s =
num5s = 0;
}
void Wallet::AddBill(int amt)
{
if (amt == 1)
num1s++;
else if (amt == 5)num5s++;
}
...
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++;
}
...
int main()
{
Wallet a, b;
int main()
{
Wallet a, b;
a.Init();
b.AddBill(5);
}
Init(&a);
AddBill(&b,5);
}
Classes and the “this” Pointer
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++;
}
...
a
num1s
num5s
0
01
1000
this 1000
amt
5
int main()
{
Wallet a;
1000
a.Init(&a);
1000 5
a.AddBill(&a , 5);
}
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
You can explicitly use the “this”
variable in your methods if you like!
While C++ hides the “this pointer” from you, if you
want,
yourfine!
class’s methods can explicitly use it.
It works
void Wallet::Init()
{
num1s
= num5s
= 0;
this->num1s
= this->num5s
= 0;
cout << “I am at address: “ << this;
}
void Wallet::AddBill(int amt)
{
if (amt == 1)
num1s++;
else if (amt == 5)
num5s++;
}
...
int main()
{
Wallet a;
}
a.Init();
cout << “a is at address: “ << &a;
Your class’s methods can use the
this variable to determine their
address in memory!
So now you know how C++
classes work under the hood!
a
num1s
num5s
0
0
1000
I am at address: 1000
a is at address: 1000
16
Returning (A Pointer to) Yourself
Challenge: What does the
following program print?
class Person {
public:
...
Person* get_me()
{ return this; }
string get_name()
{ return name_; }
private:
string name_;
};
Thinking time!
Answer: It prints “Hi Antoine”
This tells the function to
return theLet’s
address
of
see
Now p points at the object itself! why!
object a.
a
int main(){
Person a(“Antoine”);
p
CSNerd *p = a.get_me();
cout << “Hi “ <<
p->get_name();
}
// prints “Hi Antoine”
class Person {
public:
...
Person* get_me()
{ return 3004
this; }
string get_name()
{ return name_;}
};
name_ “Antoine”
...
03002
03004
03006
03008
03010
03012
03014
03016
03018
03020
03022
...
17
Returning Yourself
class Person {
public:
...
Person& pget_me()
{ return *this; }
string get_name()
{ return name_; }
p is now an
“alias”
for
private:
variable
a! name_;
string
};
int main(){
Person a(“Antoine”);
CSNerd &p = a.get_me();
cout << “Hi “ <<
p.get_name();
}
// prints “Hi Antoine”
No copy
of the variable
is made
Challenge:
What does
the –
the object
literally
returns
itself.
following
program
print?
Thinking time!
Answer: It still
prints “Hi Antoine”
This tells the function
Let’s see why!
to return a reference
to the object itself!
...
While I showed the 03002
object
a class Person {
03004
moving, no data is actually
03006
public:
being moved. 03008
...
03010
Person& get_me()
{ returna*this;
} 03012
Instead,
reference
to the
03014
string get_name()
object
is returned and03016
this is
{ return name_;}
assigned to p. 03018
name_ “Antoine”
03020
};
03022
...
18
Just like every variable, every
function has an address in
memory too!
Pointers… to Functions?!?
void squared(int a)
3000
{ cout << a*a;}
100
3050
3100
void cubed(int a)
3150
{ cout << a*a*a;}
8
3200
3250
3300
3350
3400
3450
3500
int main()
3550
{
3600
3650FuncPtr f;
3700
3000
f
=
&squared;
3750
3800f(10);10
3150
3850
f = &cubed;
3900
2
f(2);
}
3950
YES! Just as you can have
pointers to variables, in
C++ you can also have
pointers to functions!
Let’s gloss over the syntax
for a second, and just see
how it might work…
f
This we
linedefine
gets the
First
a address
Now
weour
cansquared()
use the function
of
function
pointer.
function pointer
justitlike
and puts
into f.
can hold
the address
a It
regular
function
call!
of… a function!
19
3000
3050
3100
3150
3200
3250
3300
3350
3400
3450
3500
3550
3600
3650
3700
3750
3800
3850
3900
3950
Pointers… to Functions?!?
void squared(int a)
{ cout << a*a;}
void cubed(int a)
{ cout << a*a*a;}
int main()
{
FuncPtr
f;
void (*f)(int);
f = &squared;
f(10);
f = &cubed;
f(2);
}
YES! Just as you can have
pointers to variables, in
C++ you can also have
pointers to functions!
Let’s gloss over the syntax
for a second, and just see
how it might work…
And here’s the real
syntax to declare a
function pointer...
Oh, and you
can worry
leave about the
Don’t
out the & ifsyntax
you like.
right now…
It works theJust
sameremember
way!
the
concept.
20
And now it’s time for your favorite game!
John Mauchly – proposed shortcode, the first high-level programming language
for an electronic computer, first run on a UNIVAC 1 Serial 1 in 1950
Dynamic Memory Allocation…
What’s the big picture?
Often a program won’t know how much memory it
needs to solve a problem until it’s actually running.
In these cases, C++ lets you reserve a chunk of
memory on-demand and gives you a pointer to it.
…
3.14 002000
Your code can use the
2.718 002004
memory via the pointer.
6.022 002008
When you’re done, you then
42 002012
…
ask C++ to unreserve it!
C++: “Ok,
free10k
1337 011996 “Hey
C++Uses:
II’ll
need
itslots
for someone
of allocation
RAM”
void computeSomethingImportant(int count)
Dynamic
else
to
use…”
{
is used in video
ptr
C++:
“Sure,
I
ptr = askC++ForThisMuchMemory(count);
games,
word
2000
operateOnTheMemoryAt(ptr);
reserved
slots
processors,
tellC++WereDoneWithThisMemory(ptr);
“Hey
C++search
I’matdone
with
starting
address
engines,
}
etc.
the 2000
RAMetc.,
at 2000”
for
you!”
Reserved!
{
22
New and Delete (For Arrays)
Let’s say we want to define an array, but we won’t know how big
to make it until our program actually runs …
The new command can be used to allocate an
arbitrary
amount
Your pointer
variable
willof memory for an array.
int main()
{
int *arr;
int size;
then be assigned the address
How do you use it?
of
the
new array.
#1
1. First, define a new pointer variable.
So it points
toThen
the
new
array!tothe size of the
Note:
Don’t
forget
2.
determine
cin >> size;
#2
include
array brackets
you need.
arr = new int[size];
arr[0] = 10;
arr[2] = 75;
delete [] arr;
}
#4
#5
delete
] ptr;
3. Then[ use
the new command to
reserve the memory. Your pointer
if you’re deleting an
gets the address of the memory.
array…
4. Now use the pointer just like it’s
an array!
5. Free the array when you’re done.
#3
23
New and Delete (For Arrays)
int main()
{
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 type
is the same as the type of
array you’re creating!
24
New and Delete (For Arrays)
int main()
{
int *arr;
int size;
…
arr
cin >> size;
3
4 * 3 = 12 bytes
arr = new int[size];
arr[0] = 10;
// etc
delete [] arr;
}
First, the new command determines how
much memory it needs for the array.
size
3
00001000
00001001
00001002
00001003
00001004
00001005
00001006
00001007
00001008
…
25
New and Delete (For Arrays)
int main()
{
int *arr;
int size;
…
arr
cin >> size;
Ok, ok. Dr. Operating
Yo dawg – can you reserve 12
System… can you reserve 12
bytes of memory for me?
bytes of memory for me?
4 * 3 = 12 bytes
arr = new int[size];
arr[0] = 10;
// etc
delete [] arr;
}
Next, the new command asks the
operating system to reserve that
many bytes of memory.
size
3
00001000
00001001
00001002
00001003
00001004
00001005
00001006
00001007
00001008
…
00030050
00030051
That’s
better.
Ok,
I found
Yo
Dawg?
You will
address
00030052
of free
memory
me12
asbytes
Dr. Operating
System
00030053
at address
30050.
or NO
memory00030054
for you.
...
...
00030060
26
New and Delete (For Arrays)
int main()
{
int *arr;
int size;
…
arr
You
your
Youcan
cannow
alsotreat
use the
pointer just
likelike
an (instead
array!
* notation
if you
cin >> size; (i.e. use [ ] to index it)
of brackets)
4 * 3 = 12 bytes
arr = new int[size];
arr[0]
10;// arr[0] = 10;
*(arr+0) =
= 10;
//
etc = 20; // arr[1] = 20;
*(arr+1)
00001000
30050 00001001
00001002
00001003
size
00001004
3 00001005
00001006
00001007
00001008
…
delete [] arr;
}
Finally, your pointer variable gets the
address of the newly reserved memory.
10
...
00030050
00030051
00030052
00030053
00030054
...
00030060
27
New and Delete (For Arrays)
… not the pointer variable
itself!
int main()
Our pointer variable still holds
{
the address of the previouslyint *arr;
reserved memory slots!
int size;
cin >> size;
arr = new int[size];
Operating System – I’m
}
}
done with my 12 bytes of
arr[0] = 10;
Note:
When
you
memory at
location
30050.
// etc
use the delete
command, you
delete [] arr;
free the pointedCRASH!
arr[0] = 50;
to memory…
arr
…
00001000
30050 00001001
00001002
00001003
size
00001004
3 00001005
00001006
00001007
00001008
…
00030050
00030051
10 00030052
Ok..
I’ll
let
someone else
When you’re done,
you
use the
But
they’re
no longer
00030053
now…
for array.
this program! use that memory
delete command reserved
to free the
00030054
So don’t try to access them
...
...
Usage: delete [] ptrname;
or bad things will happen!
00030060
29
New and Delete (For Non-Arrays)
struct Point
{
intdynamically
x;
We can use new and delete to
allocate
individual values and arrays int
of y;
any type of value!
};
int main()
{
For example, we can allocate
Operating System – can
// define our pointer
a of
struct value like this….
you
reserve
8
bytes
Point *ptr;
memory for me?
// allocate our dynamic variable
ptr = new Point;
}
// use our dynamic variable
ptr
30050
Operating
System
–
I’m
ptr->x = 10;
00030050
Ok.. I’ll
let someone
done
with
my
8
bytes
of
x
Ok.. I found
8 bytes
of
(*ptr).y = 20;
10
00030052
else
use
that
memory
memory at location 30050. free memory at address
now… 00030054
y
// free our dynamic variable
30050.
20 00030056
delete ptr;
00030058
...
...
30
class Nerd
{
public:
IQ, float
GPA) {
We can use new and delete toNerd(int
dynamically
allocate
individual values and arrays of m_myIQ
any type= IQ;
of value!
m_myGPA = GPA;
}
void saySomethingNerdy() {
int main() {
cout << “C++
For example,
werocks!”;
can allocate
//
// define
define our
our pointer
pointer
a}struct value like this….
Point
Nerd *ptr;
*ptr;
…
};
Then calls the Nerd
//
// allocate
allocate our
our dynamic
dynamic variable
variable
Orconstructor
we can evenwith
allocate
ptr
ptr == new
new Point;
Nerd(150, 3.999);
thesea
New and Delete (For Non-Arrays)
//
// use
use our
our dynamic
dynamic variable
variable
ptr->x
= 10;
ptr->saySomethingNerdy();
(*ptr).y = 20;
class instance
this….it!
parameters
to like
initialize
This allocates
enoughvariable
memory for a
// free
free our
our dynamic
dynamic
variable
//
delete ptr;
ptr;
delete
Nerd object...
}
31
What Exactly is Deleted?
class Dog { ... };
int main() {
Dog *p;
p = new Dog("Koda");
...
The pointer still
delete p;
refers to the
} p = new Dog("Emi");
same location.
}
p 30050
Does it
free
the
pointer?
X
Dog
object
...
When we use delete does it
free the pointer or the object
pointed to by the pointer?
Thinking time!
Answer: It frees the object,
NOT the pointer!
But the data
The pointer continues to exist
there is no
00030050 and point to the same address!
longer valid.
00030051
Or the
00030052
object? You can even re-use the pointer
00030053 and point it at another object!
00030054
...
00030060
32
Using new and delete in a class
Step #2:
Step
#3:
Update our
constructor
so
Usepass
the new
command to
class PiNerd
the user can
in So
the
how we
might use
Step
#4:
Let’s
assume
we’re
given
a
allocate
an
array
of
the
right
{
size of the nerd’s new/delete
array.
within
a class?
Update
our
loop
so
we
compute
function
that
can
give
us
any
size.
Remember
its
size!
public:
Well,
here
we have
a class that
the
first
n square
numbers
digit
of π.
PiNerd()
{ {
PiNerd(int n)
represents
who100)
like to
(instead
of justpeople
the first
m_pi = new int[n];
// alloc
Step
#6:array
memorize π – PiNerds!
m_n = n;
store its size!
Add //
a destructor
that frees the
for (int j=0;j< m_n
10 ;j++)
dynamic
array when we’re
done!
As you
can see, right now Pi Nerds
m_pi[j] = getPiDigit(j);
can only memorize up to the
}
first 10 digits of π.
Cool! Now we can have
~PiNerd() {
PiNerds of varyingStep #5:
update our class so they can
delete [] m_pi; // free memoryUpdate Let’s
our
loop
so we print
nerdiness!
memorize
as
many digits as they like!
}
out all N numbers.
int main()
Step #1:
void showOff()
Change our fixed array{
{
to a pointer
variable andPiNerd notSoNerdy(5);
for (int j=0;j<
10 ;j++)
m_n
PiNerd superNerdy(100);
add a size
cout << m_pi[j]
<<variable.
endl;
}
notSoNerdy.showOff();
private:
superNerdy.showOff();
int m_pi[10]; int *m_pi, m_n;
}
};
33
Let’s Play….
Programming Language Inventor
Or
Serial Killer
Bertrand Meyer: invented eiffel programming language
34
Copy Construction
* Note: This meme has nothing to do with copy construction.
Copy Construction…
What’s the big picture?
Copy construction is when we create
(construct) a new object by copying the
value of an existing object.
void cloneANerd()
{
PiNerd existingNerd(4); // knows PI to 4 digits
…
Create a
new variable
PiNerd clonedNerd = existingNerd;
}
By copying an
existing variable
clonedNerd.showOff(); // prints 3.141
clonedNerd
existingNerd To clone more complex
}
3
1
4
1
5
objects, we need to
create a special function
to do this, called a copy
constructor.
Uses:
Copy constructors
are required if
you want to let
users make a copy
of more complex
class variables.
Copy Construction
36
The parameter to our new In the past, our constructors
class CSNerd constructor
{
took simple arguments like
is an existing
public:
strings and ints.
CSNerd that we want to clone!
CSNerd(string name, int IQ) {
{ name_ = name;
IQ_
= IQ;
name_
= name; IQ_ = IQ;
}
CSNerd( const CSNerd& old ) {
name_ = old.name_;
IQ_ = old.IQ_;
That
}
makes my
head
spin!a
Creates
...
private:
string name_, IQ_;
};
But we can also create a
constructor that initializes a
new object based on an existing
object of the same type!
Then
we lingo,
just copy
member
In C++
this the
function
is
variables
from
the
original
called a “copy constructor.”
By copying
object to the new
object.an
existing
int main() {
CSNerd!
new CSNerd…
CSNerd a("David", 200);
CSNerd b(a);
}
37
Copy Construction
class CSNerd {
The parameter to your
public:
This is a promise
that
youthe
won’tcopy constructor must be
We must
pass
CSNerd(string name, int IQ) {
const!
modify the
old variable
while
original
object
by
{ name_ = name;
constructing your new variable!
IQ_
= IQ;
name_
= name; IQ_ = IQ;reference.
The type of your parameter
}
must be the same type as
CSNerd( const CSNerd& old ) {
name_ = old.name_;
IQ_ = old.IQ_;
} old.IQ_ -= 10; // error–const!
}
...
private:
string name_, IQ_;
};
the class itself!
The parameter to your copy
constructor must be a
reference!
38
WAIT!!! Variable b is accessing the
private members of variable a!
Copy Construction
Isn’t that violating C++ privacy rules?
class CSNerd {
b class CSNerd {
public:
...
CSNerd(const CSNerd&
CSNerd(string
name,old)
int{ IQ) {
name_ = old.name_;
= name;
{ name_
IQ_ = old.IQ_;
IQ_
= IQ;
name_
= name; IQ_ = IQ;
}
}
...
CSNerd( const CSNerd& old ) {
private:
name_ = old.name_;
name_
IQ_
IQ_
=
old.IQ_;
}}
Ok let’s trace through it!
a
class CSNerd {
...
CSNerd(string name, int IQ) {
name_ = name;
IQ_ = IQ;
}
...
private:
name_
}
David
This means:
“Initialize variable b based
int main() {
on the value
of variable
“private” protects
a class's
membersa.”
That’s not a problem!
...
from unrelated classes/functions!
200
CSNerd a("David", 200);
private:
So a CSNerd object can’t touch an
CSNerd b(a);
string name_, IQ_;
EENerd’s privates (for obvious reasons) }
};
but every CSNerd object can touch
every other CSNerd object’s privates.
IQ_
39
Copy Construction
class CSNerd {
...
CSNerd(const CSNerd& old) {
name_ = old.name_;
IQ_ = old.IQ_;
}
...
private:
string name_, IQ_;
};
int main() {
CSNerd a("David", 200);
b(a);
CSNerd b
= a; // same!
}
Oh, C++ also allows you to
use a simpler syntax…
Instead of writing:
CSNerd b(a);
which is ugly…
You can write:
CSNerd b = a;
Even though it looks like an
assignment, it still calls the
copy constructor!
40
Copy Construction
This calls the copy
constructor...
class CSNerd {
...
CSNerd(const CSNerd& old) {
name_ = old.name_;
IQ_ = old.IQ_;
}
CSNerd b(a);
to create a
copy for our
It’s used any time you make a
parameter
new copy of an existing object.
variable.
...
private:
string name_, IQ_;
};
void foo(CSNerd n) {
...
}
The copy constructor is not just
used when you initialize a new
object from an existing one:
When we pass an
object by value...
int main() {
CSNerd a("David", 200);
foo(a);
}
When else might a copy
constructor be used?
Thinking
time!
Answer:
When we
pass an
object by value to a function
41
Copy Construction
But then why would I
ever need to define my
b name_
own copy constructor?
IQ_
If you don’t define your
own copy constructor…
class CSNerd {
public:
CSNerd(string name, int IQ) {
name_ = name;
IQ_ = IQ;
}
// No
copy constructor!
Secretly
added by compiler
CSNerd(const CSNerd& old) {
name_ = old.name_;
IQ_ = old.IQ_;
}
a name_ “Al”
C++ will provide aIQ_
default
110
one for you…
It just copies all of the
member variables from the old
instance to the new instance…
This is called a “shallow copy.”
...
Carey says:
private:
string
name_, IQ_;
“Patience,
grasshopper!
}; I’ll show you in a minute!”
int main()
{
CSNerd a(“Al”,110);
CSNerd b(a);
}
42
Copy Construction
Ok – so why would we ever need to write our own
Copy Constructor function?
After all, C++ shallow copies all of an object's member
variables for us automatically if we don’t write our own!
Let's see with our PiNerd class!
43
Copy Construction
class PiNerd
{
public:
3
PiNerd(int n) {
m_n = n;
m_pi = new int[n];
for (int j=0;j<n;j++)
m_pi[j] = getPiDigit(j);
}
~PiNerd(){delete []m_pi;}
int main()
{
PiNerd ann(3);
if (...)
{
PiNerd ben = ann;
...
}
ann.showOff();
}
void showOff()
ann m_n 3
{
for (int j=0;j<m_n;j++)
m_pi 800
cout << m_pi[j] << endl;
}
private:
int *m_pi, m_n;
};
3
1
4
00000800
00000804
00000808
44
Copy Construction
Now, watch what happens when
C++’s default copy constructor
class
PiNerd
shallow
copies ann’s member
{
variables into ben…
public:
PiNerd(int n) {
m_n = n;
m_pi = new int[n];
for (int j=0;j<n;j++)
m_pi[j]
= getPiDigit(j);
Both
ann’s m_pi
}
int main()
{
Point to ann’s
PiNerd ann(3); original copy
if (...)
of the array!
{
PiNerd ben = ann;
...
}
pointer…
~PiNerd(){delete []m_pi;}
ann.showOff();
}
void showOff()
ann m_n 3
{
for (int j=0;j<m_n;j++)
m_pi 800
cout << m_pi[j] << endl;
}
And ben’s m_pi
ben m_n 3
private:
pointer…
int *m_pi, m_n;
m_pi 800
};
3
1
4
00000800
00000804
00000808
45
Copy Construction
class PiNerd
When ben is
{
public:
destructed…
PiNerd(int n) {
m_n = n;
m_pi = new int[n];
for (int j=0;j<n;j++)
m_pi[j] = getPiDigit(j);
}
~PiNerd(){delete []m_pi;}
int main()
{
PiNerd ann(3);
if (...)
{
PiNerd ben = ann;
...
} // ben's d’tor called
ann.showOff();
}
void showOff()
ann m_n 3
{
for (int j=0;j<m_n;j++)
m_pi 800
coutIt<<ends
m_pi[j]
<<
endl;
up freeing
}
private: the same array that’s ben m_n 3
int *m_pi,used
m_n;by ann!
m_pi 800
};
3
1
4
00000800
00000804
00000808
Now ann’s m_pi points
When happens to ann’s copy
of the memory!!!
at invalid
Thinking time!
m_pi array when ben is destructed?
46
Copy Construction
class PiNerd
{
public:
PiNerd(int n) {
m_n = n;
Now,
we try to
m_pi
= when
new int[n];
foraccess
(int j=0;j<n;j++)
ann’s array,
m_pi[j] = getPiDigit(j);
we get garbage!!!
}
~PiNerd(){delete []m_pi;}
int main()
{
PiNerd ann(3);
if (...)
{
PiNerd ben = ann;
...
} // ben's d’tor called
ann.showOff();
}
void showOff()
ann m_n 3
{
for (int j=0;j<m_n;j++)
m_pi 800
cout << m_pi[j] << endl;
}
private:
int *m_pi, m_n;
};
?
?
?
That’s a big problem!
47
Copy Construction
class PiNerd
{
public:
PiNerd(int n) {
m_n = n;
m_pi = new int[n];
for (int j=0;j<n;j++)
m_pi[j] = getPiDigit(j);
}
~PiNerd(){delete []m_pi;}
void showOff()
{
for (int j=0;j<m_n;j++)
cout << m_pi[j] << endl;
}
private:
int *m_pi, m_n;
};
What’s the moral of the story?
Bad things happen when a new
object refers to the same data
as the old object.
ann
m_n
3
m_pi 800
ben
m_n
3
1
4
000800
000804
000808
3
m_pi 800
Unfortunately, a default copy
constructor will often cause
this to happen...
Copy Construction
48
So what do we want instead?
A copy constructor must ensure the new object is a
completely independent clone of the old object.
The two objects must be logically identical, but
must not share any data structures in memory.
No data held by the new object
may refer to the old object.
BAD!
While the objects have
different data inside,
they’re
identical!
For ourlogically
PiNerd example,
this is what we’d like to see:
GOOD!
49
Copy Construction
For classes that point to external objects/arrays, you
must define your own copy constructor:
Step #1
Determine what resources are used by the source object
Step #2
Allocate a matching set of resources for the target object
Step #3
Copy the contents of the source object to the target object so
it’s functionally identical, but 100% independent
PiNerd ben(ann);
ann
When we’re done, all data
held by the new object
must be independent of
the old object.
m_n
3
m_pi 800
ben
m_n
3
m_pi 900
3
1
4
00000800
00000804
00000808
3
1
4
00000900
00000904
00000908
The Copy Constructor
50
int main()
class PiNerd
{
{
PiNerd ann(3);
public:
This ensures
the new
if (...)
PiNerd(int n) { … }
~PiNerd(){ delete[]m_pi; } object {has its own
independentPiNerd
memoryben = ann;
...
// copy constructor
to store its
data.
}
PiNerd(const PiNerd &src)
{
ann.showOff();
m_pi = new int[src.m_n];
}
and allocates
its own array!
First, the new object
determines how much
void showOff() { ... }
memory it needs...
}
private:
int *m_pi, m_n;
};
Let’s see how to define our
copy constructor!
51
The Copy Constructor
class PiNerd
{
public:
PiNerd(int n) { … }
~PiNerd(){ delete[]m_pi; }
// copy constructor
PiNerd(const PiNerd &src)
{
m_pi = new int[src.m_n];
m_n = src.m_n;
for (int j=0;j<m_n;j++)
m_pi[j] = src.m_pi[j];
}
void showOff() { ... }
private:
int *m_pi, m_n;
};
Let’s see how to define our
copy constructor!
int main()
{
PiNerd ann(3);
if (...)
{
PiNerd ben = ann;
...
}
ann.showOff();
}
Next, the new object
copies over the data
from the source object.
52
The Copy Constructor
class PiNerd
{
public:
PiNerd(int n) { … }
~PiNerd(){ delete[]m_pi; }
// copy constructor
PiNerd(const PiNerd &src)
{
m_pi = new int[src.m_n];
m_n = src.m_n;
for (int j=0;j<m_n;j++)
m_pi[j] = src.m_pi[j];
}
void showOff() { ... }
private:
int *m_pi, m_n;
};
Let’s watch our correct copy
constructor work!
int main()
{
PiNerd ann(3);
if (...)
{
PiNerd ben = ann;
...
}
ann.showOff();
}
src
ann
m_n
3
m_pi 800
ben
m_n
3
m_pi 900
3
1
4
3
1
4
00000800
00000804
00000808
00000900
00000904
00000908
53
The Copy Constructor
class PiNerd
{
public:
PiNerd(int n) { … }
~PiNerd(){ delete[]m_pi; }
// copy constructor
PiNerd(const PiNerd &src)
{
m_pi = new int[src.m_n];
m_n = src.m_n;
for (int j=0;j<m_n;j++)
m_pi[j] = src.m_pi[j];
}
void showOff() { ... }
private:
int *m_pi, m_n;
};
int main()
{
PiNerd ann(3);
if (...)
{
PiNerd ben = ann;
...
}// ben's d’tor called
3
1
4
ann.showOff();
}
00000800
3
3
1 00000804
m_pi 800
4 00000808
ben m_n 3
00000900
3
We’re OK, since1 ann00000904
still
m_pi 900
has its own 4
array!
00000908
ann
m_n
Copy
Construction:
Summary
Ben's data structures
54
are functionally/logically
With copy construction, we are creating a clone of an
identical to ann's!
existing variable using a constructor.
PiNerd ben(ann);
ben
m_n
3
m_pi 700
3
1
4
000700
000704
000708
ann
m_n
3
m_pi 800
3
1
4
000800
000804
000808
Yet they are totally
independent from the
originalthis
data
structures.
In practice,
entails
creating a new set of data structures and
copying the data from the old data structures to the new ones.
What's critical is that your new object behaves just like the original,
yet its data structures are completely independent from the original's.
Be careful! It's not always as simple as just allocating a new
dynamic array and copying a few values over!
Download