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!