1 / 39

CSE 143

CSE 143. Dynamic Memory [Chapter 7]. Dynamic Memory: Intro. Problem: All of our data structures so far have a “maximum” size. This size is fixed at compile time .

art
Télécharger la présentation

CSE 143

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. CSE 143 Dynamic Memory [Chapter 7]

  2. Dynamic Memory: Intro • Problem: All of our data structures so far have a “maximum” size. • This size is fixed at compiletime. • Often this is acceptable, but many real applications need to grow and shrink the amount of memory consumed by an ADT at run time. • Most languages provide some form of dynamic memory. • C++ provides an interface to dynamic memory via two new operators: new and delete.

  3. Data and Memory • Objects of different types may requires differing amounts of memory • Built-in types: implementation dependent • PC (typical): • char: 1 byte (8 bits) • int: 4 bytes (2 bytes on older systems) • double: 8 bytes • Programmer defined types: depends on size of data members • could be few bytes or thousands of bytes

  4. Ways of Using Memory • Static - allocated at program startup time, exists throughout the execution of the entire program • Automatic - implicitly allocated upon function entry, deallocated on exit • void foo (char x) { • int temp; • . . . • // x and temp are deallocated here • } • Dynamic - explicitly allocated and deallocated by the programmer

  5. Pointers: Review • Every memory cell in a computer’s memory has two characteristics: • A value (contents) • A unique address • Every object in the program occupies a range of memory cells • Suppose we have: • int x; • To access the value of x: • x • To access the address of x: • &x

  6. Pointer Variables • By "address of an object" we mean the address of the first memory cell used by the object • A pointer variable is one that contains the address of another data object as its value. • To declare a pointer variable: • Type* name; • Example: • int* intPtr; • char* charPtr; • BigNat* bigNatPtr;

  7. Pointers and Types • Pointers to different types themselves are different types double *dpt; BankAccount * bp; • C/C++ considers dpt and bp to have different types • even though under the hood they are both just memory addresses • Types have to match in many contexts • e.g. actual param types matching formal param types • pointers are no exceptions

  8. Two Important Operators • The address-of operator &: • int x = 45; • int* p = &x; • The dereference operator *: • *p = 30; • p = 72; // what’s the problem here?

  9. The NULL pointer • During program execution, a pointer variable can be in one of the following states: • Unassigned • Pointing to a data object • Contain the special value NULL • The constant NULL is defined as 0 in stddef.h, and is used to mean a pointer that does not point to any object. • NULL is compatible with all pointer types • Style factoid: some programmers like NULL, others use 0.

  10. Pointer Types & Ops • Domain (possible values) • The set of all memory addresses along with the NULL pointer • Operations valid on pointers of all types (pp292-293). We’ll cover only a subset: = (assignment) • int* p = &someInt; * (dereference) • *p = 345; == (equality test) • if (ptr1 == ptr2) { . . . }

  11. More Pointer Operations != (test for inequality) • if (ptr1 != ptr2) { . . . } delete (deallocate) • delete ptr; // more on this later -> (select a member of a pointed-to object) • void foo (BankAccount* b) { • b->printBalance(); • }

  12. Reference Types • We can declare variables or params of reference types: • Type& rname //rname will hold an alias to something • //of type Type • Example: • int x; • int& refx = x; // a ref. variable must be initialized • x = 40; • cout << refx; // what’s the output? • refx = 20; • cout << x; // what’s the output? • Main use: for parameters

  13. Writing Swap (Old School) • In 142, you used pointers to write functions which side-effected their actual parameters: • void swap(int* p, int* q) { • int temp; • temp = *p; • *p = *q; • *q = temp; • } • // example call: • swap(&intOne, &intTwo); // don’t forget the &

  14. Writing Swap (New School) • C++ lets us use reference parameters, leading to much cleaner code: • void swap(int& a, int& b) { • int temp = a; • a = b; • b = temp; • } • // example call: • swap(intOne, intTwo); // note: no &

  15. Allocation & Deallocation • Allocate dynamic memory with the new operator: • The expression new Type returns a pointer to a newly created object of type Type: • int* p; • char* str; • p = new int; // allocate a single int • str = new char[10]; // allocate an array of chars • Deallocate memory with the delete operator: • delete Pointer deallocates the object pointed to by Pointer • delete p; // deallocating a simple object • delete [] str; // deallocating an array of objects

  16. The Heap • Objects created by new come from a region of memory set aside for dynamic objects (called the heap, or free store). • The new operator obtains a chunk of memory from the heap; delete returns that memory to the heap. • In C++ the programmer must manage the heap. • Dynamic data is unnamed and can only be accessed through pointers.

  17. Two Pesky Problems • Inaccessible objects and memory leaks • An inaccessible object is a dynamic object without any pointers pointing to it • Sometimes called garbage. • Accumulating garbage leads to a memory leak. • Dangling pointers • A pointer that points to where an object used to be (even though the object's memory has been deallocated) is a dangling pointer. • Both are serious problems • dangling pointer is especially dangerous

  18. Garbage (memory leak) • Example • int* p; • p = new int; • *p = 45; • p = new int; //! • *p = 55; • Example 2 • int *p, *q; • p = new int; • q = new int; • *p = 45; • *q = 55; • p = q; //! local vars. heap p p q

  19. Dangling Pointers • Example 1 • int *p = new int; • int *q; • *p = 45; • q = p; • delete p; • *q = 55; // oops! • Example 2 • char* broken() { • char buffer[80]; • cin >> buffer; • return buffer; • } • charPtr = broken(); // intPtr is dangling local vars. heap p q Destroyed when function exits!!

  20. Dynamic Data Guidelines • Avoid creating garbage when invoking new or moving pointers. • The new operator returns a pointer to an object. If the object’s type is a class, then the constructor has been called. If not, the object is still uninitialized. • Don’t dereference an unassigned pointer. • After delete, the pointer value is unassigned (don't assume NULL)

  21. Example for DM: A Vector Class • In C++, built-in arrays have several shortcomings: • When declaring an array, the programmer must state its size in advance. • Out-of-bounds subscripts are not detected at run-time. • int v[20]; • v[i] = 100; // big oops if i is out of bounds. • There is no aggregate assignment of one array to another: • int a[20], b[20]; • a = b; // error • Arrays can’t be returned as function values. • These can be overcome • by defining our own C++ class with the desired behavior.

  22. A Vector Class • We would like to have a class that allows: • cout << “input array size”; • cin >> n; • IntVector u(10); // a 10 element "array" • IntVector v(n); // an n element array • IntVector w(n); • u[2] = v[3]; //normal array [] notation • w = v; //assign entire vector

  23. First Try • class IntVector { • public: • IntVector(int n); // constructor • int& operator[](int i); // subscript • private: • int *vec; • int size; • };

  24. Basic Constructor • IntVector::IntVector(int n) { • assert (n>0); • size = n; • vec = new int[size]; • }

  25. Overloading [ ] • Clients want to write code like this: • IntVector a(3); • IntVector b(4); • a[2] = b[3]; • Need to overload subscripting: • int& • IntVector::operator[] (int i) { • assert(i>=0 && i<size); // range check • return vec[i]; • } • Why do we return a reference to integer?

  26. Problem 1 (Shallow copying) • We can now write: • IntVector v1(5); • IntVector v2(5); • v1[1] = 100; • v2[3] = v1[0] = v1[1]; • Now suppose: • v1 = v2; // what happens??

  27. Shallow Copying • The default assignment operator (=), just performs memberwise assignment • This results in a shallow copy • Here is a picture just before v1 = v2: heap space: v1 v2

  28. Problems w/ Shallow copies • Shallow copying causes instances to share data, which may be undesirable: • Vector v1(10); • Vector v2(10); • v2 = v1; • v1[3] = 17; // what does v2[3] contain now? • Deep copying gives each instance its own copy: • void IntVector::operator=(IntVector& other) • { • Destroy(); // free my dynamic data • Copy(other); // get a copy of other’s data • }

  29. Private Helper Functions • Unconditionally copy the other vector: • void IntVector::Copy(IntVector& other) { • vec = new int[other.size]; • size = other.size; • for (int i=0; i<size; i++) • vec[i] = other.vec[i]; • } • Unconditionally deallocate data: • void IntVector::Destroy() { • delete [] vec; • vec = NULL; • }

  30. Safer Assignment • Self-assignment is a problem. Why? • v1 = v1; // shouldn’t self assign… • Ever object has a special data member, a pointer called this, which is a pointer to the object itself. • void IntVector::operator=(IntVector& other) • { • if (this != &other) { • Destroy(); // free my dynamic data • Copy(other); // get a copy of other’s data • } • }

  31. Destructors & Memory Leaks • A destructor is a special method automatically invoked when the class instance is destroyed. • A class instance is destroyed when it goes out of scope: • void fun (IntVector v1) { • IntVector v2(10); • . . . • } // v1 and v2 are destroyed here • What happens to the private data of v1 and v2? • What happens to the memory pointed to by v1 and v2?

  32. Problems • Automatic data • automatically deallocated at end of block (e.g. when a function exits.) • no problem • Heap space, however, is managed by the programmer. • never automatically deallocated • If we don’t free the memory pointed to by our object, we’ll get a memory leak.

  33. Destructors (3) • We can write our own destructor which frees the memory our object points to: • IntVector::~IntVector() { • Destroy(); • } • There should be only one destructor per class. • The destructor takes no arguments. • Use [ ] for an array of instances, to assure that a destructor is called for each element.

  34. Problem 2 (More Shallow Copying) • Suppose: • void DoSomething(IntVector v1) { • . . . • } • . . . • IntVector v(10); • DoSomething(v); • When we pass an object to a function, the formal parameter is a shallow copy of the actual parameter. • Can you see why this code is broken? (Hint: think about the destructor we just wrote!)

  35. Copy constructors • The same problem with deep vs. shallow copying can also appear in this context: • Initialization in a variable declaration: • SomeClass foo = bar; • Passing a copy of an actual to a formal parameter (pass-by-value) • Returning an instance as the value of a function: • return someIntVector; • By default, C++ performs such initializations using shallow copy semantics.

  36. Copy constructors (2) • To define your own way of copying, you can write a copy constructor, which has a special syntax: • // General form of copy constructor: • SomeClass::SomeClass(SomeClass& other); • IntVector::IntVector(IntVector& other) { • Copy(other); • } • Now when we pass-by-value, the formal parameter will be deep copy of the actual. • Because compiler will call the copy constructor instead of doing a shallow copy

  37. Full IntVector Specification • class IntVector { • public: • IntVector(int n); // contstructor • IntVector(IntVector& other); // copy cons • ~IntVector(); // destructor • int& operator[](int i); • void operator=(IntVector& other); • private: • int* vec; • int size; • void Copy(IntVector& other); • void Destroy(); • }

  38. Classes w/ dynamic data • If a class allocates data on the heap, it probably needs the following suite of member functions to ensure proper management of dynamic data: • constructor - to create data on the free store • copy constructor - for deep copying during initialization and pass-by-value • destructor - to release memory back to the free store • assignment operator - for deep copying during assignment

  39. Example Client Code • #include “IntList.h” • void main() { • int size; • cout << “Input size”; • cin >> size; • IntVector v1(size),v2; • for (int i=0; i<size; i++) • v1[i] = i; • v2 = v1; // make a copy • for (int j=0; j<size; j++) • cout << v2[j] << “,”; • }

More Related