week12.pdf
Engineering 280 with Lipman/mao at University of Michigan - Ann Arbor
About this note
By: Arun Ganesan
Created: 2008-05-31
File Size: 7 page(s)
Views: 5
Created: 2008-05-31
File Size: 7 page(s)
Views: 5
About StudyBlue
STUDYBLUE makes things that make you better at school.
Things like online flashcards with photos and audio.
Things like personalized quizzes and friendly reminders about when (and what) to study next.
Think of it as a digital backpack™: access to all of your study materials online and on your phone.
STUDYBLUE exists to make studying efficient and effective for every student, for free. Join us.
“I have been getting MUCH better grades on all my tests for school. Flash cards, notes, and quizzes are great on here. Thanks!”
Kathy
Kathy
Sign up (free) to study this.
EECS 280 Week 11 Discussion Notes: Deep Copies Last week, we looked all the ?simple? ways to shoot yourself in the foot with new and delete. This week, we?ll take a look at the problem of making so-called ?deep copies.? This problem is less simple than those we did last week because it involves classes with dynamically allocated members, not just dynamically allocating variables in some function. The fundamental problem is that the default copy operations provided by the language just copy what the compiler can ?see? in the object. If the object has in members, then ints are copied from the old object to the new object. Likewise, if the object has string members, the strings are copied. Accordingly, if the object has pointers as members, the pointers are copied. Most of the time, this is not what you want. You would like the new object to have a new pointer that points to a copy of the old pointed-to object. Let?s look at an example situation. Suppose we want to keep track of students? grades in 280. The number of students changes every semester and so we want to use dynamic memory to avoid guessing at the maximum capacity of the class. First, we need to define what an individual student?s grades in EECS 280 looks like: Now we can create our class to contain everyone?s grades: struct Grades { string uniquename; double midterm; double final; double project[5]; }; class EECS280 { int numStudents; Grades* gptr; public: EECS280(string unames[], int students); ~EECS280(); void setMidterm(string name, double score); void printClassGrades(); }; We use this class by providing an array of unique names and then we fill in the grades later. Here, I?ve only shown the function to set the midterm grades; you can imagine others. Now let?s write the functions for the class: The constructor just allocates memory to store grades. Note that new and delete work just fine with classes and structs, not just primitive types. We also need a destructor to avoid leaking memory. Now we can write the functions that accomplish the goal of the class: EECS280::EECS280(string unames[], int students) { numStudents = students; gptr = new Grades[numStudents]; for(int i = 0; i < numStudents; ++i) { gptr[i].uniquename = unames[i]; gptr[i].midterm = 0; gptr[i].final = 0; for(int j = 0; j < 5; ++j) { gptr[i].project[j] = 0; } } } EECS280::~EECS280 { delete [] gptr; } void EECS280::setMidterm(string name, double score) { for(int i = 0; i < numStudents; ++i) { if(gptr[i].uniquename == name) { gptr[i].midterm = score; break; } } } Finally, we can use the class: That?s wonderful, but what if we try to pass an instance of EECS280 by value? void saveToDisk(EECS280 semester) { //? } If we pass this class by value, the default copy operations will copy all the members of the EECS280 class, which are an int and a Grades*. This does not include the array pointed to by gptr! void EECS280::printClassGrades() { for(int i=0; i < numStudents; i++) { // Assume all projects are worth the same amount double ProjTotal = 0; for(int j=0; j <5; j++) { ProjTotal += gptr[i].project[j]; } // One plausible formula double classGrade = .17 ? gptr[i].midterm + .23 ? gptr[i]. final + .6 ? ProjTotal / 5; cout << gptr[ i ].uniquename << ? scored a ? << classGrade << endl; } } int main() { string un[2]; un[0] = ?Joe?; un[1] = ?Sally?; EECS280 winter08(un,2); winter08setMidterm(?Joe?, 35); winter08.setMidterm(?Sally?, 100); winter08.printClassGrades(); return 0; } winter08 semester We copy the value of the pointer, but not what it points to. Worse, when saveToDisk() returns, the destructor for semester will get called, deleting our grades. There is a similar problem if we do something like: EECS280 Fall09(un,2); Fall09 = winter08; We really want to copy the dynamic memory and the number of students, so we need to write a copy constructor and overload the assignment operator. They will both be doing nearly the same task, so we can write a common private function called copyFrom that handles the ?deep? copying. So, here is the revised class definition. numStudents 2 gptr numStudents 2 gptr Now, let?s write copyFrom(): Question ? Why is e passed by reference instead of by value? Answer ? Passing it by value would require copying it, which would mean calling the function we?re writing, leading to infinite recursion. Now the actual functions easy: class EECS280 { int numStudents; Grades ? gptr ; void copyFrom(const EECS280 &e); public : EECS280(string unames[] , int students); ?EECS280 ( ) ; EECS280(const EECS280 &e); EECS280& operator =(const EECS280 &e ); void setMidterm(string uniqname, double score); void printClassGrades(); } ; void EECS280::copyFrom(const EECS280 &e) { for(int i = 0; i < numStudents; ++i) { gptr[i].uniquename = e.gptr[i].uniquename; gptr[i].midterm = e.gptr[i].midterm; gptr[i].final = e.gptr[i].final; for(int j=0; j <5; ++j) { gptr[i].project[j] = e.gptr[i].project[j]; } } } The tricky part is the return *this line. However, it?s easy to remember if you look at the return value: it?s a reference to an EECS280, not a pointer, so we need to return a real EECS280, not a pointer. Since ?this? is a pointer, we have to dereference it and then return. The copy constructor is similar, but first we have to allocate some memory because the class hasn?t been created yet (after all, this is a constructor). Now we can write some code in main(). After our modifications, it all works fine. EECS280& EECS280:: operator =(const EECS280 &e) { //only reallocate if we need to if(numStudents != e.numStudents) { delete[] gptr; numStudents = e . numStudents ; gptr = new Grades [ numStudents ] ; } copyFrom (e) ; return ?this ; } EECS280 : : EECS280(const EECS280 &e ) { numStudents = e . numStudents ; gptr = new Grades [ numStudents ] ; copyFrom (e) ; } int main() { string un[2]; un[0] = ?Joe?; un[1] = ?Sally?; EECS280 winter08(un,2); winter08.setMidterm(?Joe?,35); winter08.setMidterm(?Sally?,100); winter08.printClassGrades(); foo(winter08); winter08.printClassGrades(); //winter08 should be unmodified from previous function un[0] = ?Conan?; un[1] = ?Jay?; EECS280 fall09(un,2); fall09.setMidterm(?Conan?,99); fall09.setMidterm(?Jay?,100); fall09.printClassGrades(); //prints for Conan and Jay fall09 = winter08; fall09.printClassGrades(); //prints for Joe and Sally return 0; } Alan Lee Microsoft Word - EECS280Discussion.docx
Back
Next
About this note
By: Arun Ganesan
Created: 2008-05-31
File Size: 7 page(s)
Views: 5
Created: 2008-05-31
File Size: 7 page(s)
Views: 5
About StudyBlue
STUDYBLUE makes things that make you better at school.
Things like online flashcards with photos and audio.
Things like personalized quizzes and friendly reminders about when (and what) to study next.
Think of it as a digital backpack™: access to all of your study materials online and on your phone.
STUDYBLUE exists to make studying efficient and effective for every student, for free. Join us.
“I have been getting MUCH better grades on all my tests for school. Flash cards, notes, and quizzes are great on here. Thanks!”
Kathy
Kathy