cse 143 class vector interface
play

CSE 143 Class Vector: Interface class Vector { public: Dynamic - PDF document

CSE 143 Class Vector: Interface class Vector { public: Dynamic Memory In Classes Vector ( ); bool isEmpty( ); [Chapter 4, p 156-157] int length ( ); void vectorInsert (int newPosition, Item newItem); Item vectorDelete (int position);


  1. CSE 143 Class Vector: Interface class Vector { public: Dynamic Memory In Classes Vector ( ); bool isEmpty( ); [Chapter 4, p 156-157] int length ( ); void vectorInsert (int newPosition, Item newItem); Item vectorDelete (int position); Item vectorRetrieve (int position); ... } 07/13/01 07/13/01 N-1 N-2 Many Ways to Implement Vector Implementation • Version 1: With Fixed length arrays class Vector { public: • Very efficient to access individual elements // constructors and other methods, as before • Limited in size, flexibility private: • Version 2: With a linked list (later) Item *items; // items[0..capacity] is space allocated for • Very flexible in size // this vector int size; // items are stored in Items[0..size-1] • Inefficient to access individual elements int capacity; //current maximum array size Have to chase pointers down the list • Here’s a third way: // might need additional private helper functions • Use an array (for efficient access) • Make the array itself "dynamic" }; Able to grow as needed 07/13/01 07/13/01 N-3 N-4 Draw the picture! Vector Constructor Vector::Vector() { // set up private variables capacity = DEFAULT_CAPACITY; size = 0; // allocate memory items = new Item[capacity]; // what goes here? } Except for this, the public methods can be the same as for the fixed array implementation. Exception: insert needs to ensure there is room to add a new item. 07/13/01 07/13/01 N-5 N-6 CSE 143 N

  2. Useful Private Functions ensureCapacity( ) class Vector { // ensure that Vector can hold at least n elements void Vector::ensureCapacity(int n) { public: // return if existing capacity is ok // constructors and other methods if (capacity >= n) private: return; // data members here... // out of space: double capacity // ensure the Vector can hold at least n elements int newCapacity = capacity * 2; void ensureCapacity(int n); if (newCapacity < n) newCapacity = n; // set size of the Vector to n elements void growArray(int n); // grow the array growArray(newCapacity); }; } 07/13/01 07/13/01 N-7 N-8 growArray( ) Now insert is easy! // Set size of vector to newCapicity // insert newItem at newPosition in Vector void Vector::vectorInsert(int newPosition, Item newItem) { void Vector::growArray(int newCapacity) { // make room Item *newItems = new Item[newCapacity]; ensureCapacity(size+1); assert(newItems != NULL); // shift data over for (int i = 0; i < size; ++i) for (int i=size; i > newPosition; --i) newItems[i] = items[i]; items[i] = items[i-1]; // store the item ... items[newPosition] = newItem; items = newItems; size++; capacity = newCapacity; } } Have we forgotten anything? 07/13/01 07/13/01 N-9 N-10 Issues with Dynamic Memory Innocence Destroyed (I) • Using dynamic memory in classes raises issues // assume Item == int • Familiar dangers: Vector v1, v2; • Dangling pointers, Uninitialized pointers, Memory leaks, v1.vectorInsert(0, 30); etc. v1.vectorInsert(1, 4); • Some new complications v2 = v1; • Many of them arise when objects are copied v2.vectorDelete(0); Copied automatically when passed as parameters, etc. Copied explicitly by programmer • Other dangers when objects are deleted • //Draw the picture and weep! Explicitly deleted, or just go out of scope • C++ has some special features to help the situation 07/13/01 07/13/01 N-11 N-12 CSE 143 N

  3. Innocence Destroyed (II) After v2=v1, Before v2.vectorDelete void add42 (Vector v) { //add 42 to front of vector v1 v.vectorInsert(0, 42); } items 30 4 size 2 //code in main capacity 5 Vector v1; v1.vectorInsert(0, 0); v2 items add42(v1); size How does v2.vectorDelete • v1 passed by value, so no harm done -- right?? 2 change the picture? • Draw the picture and weep! capacity 5 07/13/01 07/13/01 N-13 N-14 After v1.insert(0,0) After v.insert(0, 42);... in main: in add42: v1 v items items size size 1 0 2 42 0 capacity 5 capacity 5 back in main... call add42: v1 v items items size 1 size 1 capacity 5 capacity 5 07/13/01 07/13/01 N-15 N-16 Innocence Destroyed (III) Local variable goes away... void MyFunction () { tempVector (in MyFunction) Vector tempVector; //local variable items 0 42 -3 4 // build a temporary vector for whatever reason size 4 ... capacity 5 } • When a function exits now back in main... • local variables are automatically destroyed • so having a local Vector is no problem -- right? 0 42 -3 4 • Draw the picture and weep! 07/13/01 07/13/01 N-17 N-18 CSE 143 N

  4. The Culprit: "Shallow Copy" More copy problems • For structs and classes, all and only the • The problem with deep vs. shallow copying can member variables are copied also appear in these contexts: • When there’s dynamic memory, that’s not • Initialization in a variable declaration: enough SomeClass f1; SomeClass f2 = f1; • Example: the items pointer value is copied, so the • Passing a copy of an actual to a formal parameter ( pass- copy points to the same place by-value ) • Can lead to surprises and bugs • Returning an instance as the value of a function: • Solution: need a concept of "deep copy" return someIntVector; Why? because a function returns a new, temporary object • By default, C++ performs such initializations using shallow copy semantics. 07/13/01 07/13/01 N-19 N-20 Needed: Deep Copy "Deep copy" • A "deep copy" should make a complete new copy, • A deep copy makes a completely independent including new dynamic memory copy, by allocating more dynamic memory • A way to make the deep copy happen original automatically when appropriate items 0 42 -3 4 • Vector v1 = v2; size 4 • v1 = v2; • func1(v1); capacity 5 • return v1; (deep) copy • PS: this won’t solve the problem of cleaning up dynamic memory used by local variables items 0 42 -3 4 • We’ll get back to that size 4 capacity 5 07/13/01 07/13/01 N-21 N-22 Deep copy for Vector Making It Automatic • Initialize the new vector to empty. • Problem with copyVector: must be called explicitly • For each element in the vector • We need it to happen automatically in certain cases • add it to the new vector • Could be a client function • Solution: C++ allows a "Copy Constructor" • void copyVector (Vector &orig, Vector &newVec); • Will be called automatically in certain cases where an object must be initialized from an existing object • use member functions like length, retrieve, insert, etc. • Compiler recognizes it as a constructor with a • Could be a public or private member function particular parameter list: • void Vector::copy (Vector &orig); • classname ( classname &) • copies from orig to current vector • or classname (const classname &) • use private data directly 07/13/01 07/13/01 N-23 N-24 CSE 143 N

  5. Copy Constructor for listClass Inside the Copy Constructor class Vector { • It’s just a function, it can do anything! public: • But... what you normally write is a deep copy Vector ( ); • For our Vector copy constructor: Vector(Vector &); • could call a previously defined copyVector function ... • could build the new copy directly } • If you don’t define your own copy constructor, the • Compiler recognizes this as a copy constructor compiler generates a default copy constructor • Will call automatically when • Does a shallow copy • passing arguments by value • initializing variable with = in a variable declaration • copying a return value 07/13/01 07/13/01 N-25 N-26 Look at the code: Technicalities of ’=’ Vector MyVector = YourVector; Vector::Vector(Vector &other) { copy(other); } is NOT THE SAME AS // private member function: replace this Vector // with a deep copy of other Vector MyVector; void Vector::copy(Vector &other) { MyVector = YourVector; // set up private variables capacity = other.capacity; • The difference in technical terms: size = other.size; • in the first case, the object is being created // allocate memory items = new Item[capacity]; • in the second case, the object already exists assert(items != NULL); • To handle the latter case, we have to define an // copy data "overloaded assignment operator" for (int i = 0; i < size; ++i) items[i] = other.items[i]; • Syntax: Vector & Vector::operator = (Vector &other); } • The code for this function could (should) perform a deep copy. 07/13/01 07/13/01 N-27 N-28 Detour: this Overloaded operator = • A reserved word in C++ Four important steps: • Means “a pointer to the current object” 1. Test for same object: • Like a hidden parameter to member functions • if (&other != this) { /* copy code */ } • int Vector::length( Vector *this Vector *this ) { ... } Vector *this 2. Delete old dynamically allocated data • only exists in member functions! • call cleanup() function if you have one, or • Can use like any other pointer • directly: delete [] items; • Vector *vp = this; 3. Copy new data • if (vp == this) ... • copy() if you have one • return this->size; • this->capacity = this->capacity * 2; 4. Return a reference to the current object: • this->length( ) • return *this; 07/13/01 07/13/01 N-29 N-30 CSE 143 N

Recommend


More recommend