Download presentation
Presentation is loading. Please wait.
1
Dynamic Memory Allocation
Andy Wang Object Oriented Programming in C++ COP 3330
2
Allocation Memory Compile time (or static) allocation
Memory for the named variables is allocated by the compiler The exact size and type of storage must be known The array size has to be constant Dynamic memory allocation Memory allocated during run time, placed in a program segment known as the heap or the freestore The compiler does not need to know the exact size and number of items to be allocated Pointers are crucial
3
Dynamic Memory Allocation
Can allocate memory at run time But, cannot create new variables names at run time Thus, there are two steps in dynamic allocation Allocate the space Store its address in a pointer (so that the space can be accessed) Use the new operator
4
Deallocation Free up the allocated space
Compile time (automatic) variables are automatically deallocated A programmer needs to free up dynamically allocated memory Use the delete operator
5
Allocating Space with new
To allocate space dynamically, use the unary operator new, followed by the type being allocated new int; new double; new int[40]; // dynamically allocates an array of 40 ints new double[n]; // dynamically allocates an array of n // doubles, where n can be a variable
6
Allocating Space with new
new by itself allocates spaces with no names new returns the starting address of the allocated space, which can be stored in a pointer int *p; // declares a pointer p p = new int; // dynamically allocate an int and store its // address into p int x = 40; int *list = new int[x]; float *numbers = new float[x + 10];
7
Accessing Dynamically Allocated Space
For single items, dereference the pointer to reach dynamically created target int *p = new int; *p = 10; // assigns 10 to the dynamic integer cout << *p; // prints 10 For dynamically created arrays, you can use either pointer-offset notation, or standard bracket notation double *numList = new double[size]; for (int i = 0; i < size; i++) numList[i] = 0; numList[5] = 20; *(numList + 7) = 15; // same as numList[7]
8
Stack vs. Heap vs. Content
int main() { int *a, *b, *c; a = new int; b = new int; c = new int; *a = 1; *b = 2; *c = 3; cout << "stack: &a: " << &a << " &b: " << &b << " &c: " << &c << endl; cout << "heap: a: " << a << " b: " << b << " c: " << c << endl; cout << "content: *a: " << *a << " *b: " << *b << " *c: " << *c << endl; return 0; } an integer *b = content b = a heap address b &b = a stack address
9
Deallocation of Dynamic Memory
Use unary operator delete int *ptr = new int; delete ptr; // free up the space pointed by ptr Note that the name ptr can be reused ptr = new int[10];
10
Deallocation of Dynamic Memory
To deallocate a dynamic array, use this form delete [] name_of_pointer Example int *list = new int[40]; delete [] list; // deallocates the array list = 0; // reset the pointer to null We don’t want list to point to deallocated space
11
Suppose… You fail to deallocate dynamically allocated memory…
list = new int[40]; // first allocation …// no deallocation… list = new int[10]; // second allocation Result… Memory leak…
12
Dynamically Resizing an Array
Suppose you want to grow an array int *list = new int[size]; int *temp = new int[size + 5]; // create a bigger temp array for (int i = 0; i < size; i++) temp[i] = list[i]; // copy the data delete [] list; // free up the old memory location list = temp; // make list point to the new memory location
13
Dynamic Allocation of Objects
Objects can be dynamically allocated as well Fraction *fp1, *fp2, *flist; fp1 = new Fraction; // uses default contructor fp2 = new Fraction(3,4); flist = new Fraction[20]; // dynamic array of 20 Fraction // objects with default constructor used for each To deallocate delete fp1; delete fp2; delete [] flist;
14
Dot Operator vs. Arrow Operator
A dot operator requires an object name objectName.memberName // member can be data or func An arrow operator works with object pointers objectPointer->memberName
15
Dot Operator vs. Arrow Operator
You need to dereference an object pointer before using the dot operator (*fp1).Show(); An arrow operator is a nice shortcut fp1->Show();
16
Dot Operator vs. Arrow Operator
For pointers to dynamically allocated objects, the arrow operator is easiest ftp->Show(); For pointers to dynamically allocated arrays of objects, arrow operator usually is not needed flist[3].Show(); // flist is a pointer; flist[3] is an object flist[3]->Show(); // INCORRECT
17
Using Dynamic Allocation inside Classes
A motivation example Suppose we want an array as a member data Don’t want a fixed upper bound on the size How to embed a dynamic array inside a class Which can be created statically… Which means compiler needs to know the size in advance…
18
Solution Only embed the array pointer
Declare array pointers as member data Always initialize pointers in the constructor The constructor might dynamically allocate spaces (via new) and assign them to points Or, initialize pointers to null Cleanup (via delete) dynamically allocated space when done using it Can happen in a regular member function Should happen in the destructor, the last function being run before an object is deallocated
19
Tips Separate memory management tasks from the functionality/algorithmic tasks wherever possible Write member functions just for dealing with memory (allocation, deallocation, resize) Algorithmic functions call the memory handling functions More uses of new and delete, more likely to have a memory leak
20
Exampe: PhoneBook Database
Two classes that use dynamic memory allocation Entry class Represents a single entry in a phone book Uses strings (null-terminated character arrays) to store names, addresses, and phone numbers Directory class Stores a list of Entry objects in a dynamic array Provides services Add, delete, modify, search, and display entries Dynamically resize the array of Entries if needed
21
entry.h class Entry { public: Entry(); void Load(); // load data into an entry void Show() const; const char*GetName() const; private: char name[20]; phoneNumber[20]; address[20]; };
22
entry.cpp #include <iostream> #include <cstring> #include “entry.h” using namespace std; Entry::Entry() { strcpy(name, “ “); strcpy(phoneNumber, “ “); strcpy(address, “ “); }
23
entry.cpp void Entry::Load() { cout << “\nType name, followed by ENTER: “; cin.getline(name, 20); cout << “\nType phone number, followed by ENTER: “; cin.getline(phoneNumber, 20); cout << “\nType address, followed by ENTER: “; cin.getline(address, 20); }
24
entry.cpp Null-terminated string
void Entry::Show() { int i; cout << ‘\t’ << name; for (i = strlen(name) + 1; i < 20; i++) cout.put(‘ ‘); cout << ‘\t’ << phoneNumber; for (i = strlen(phoneNumber) + 1; i < 20; i++) cout.put(‘ ‘); cout << ‘t’ << address; cout << endl; } const char* Entry::GetName() const { return name; Print blanks to format the output
25
directory.h #include “entry.h” class Directory { public: Directory(); // setup empty directory of entries ~Directory(); // deallocate the entry list void Insert(); // insert a new entry void Lookup() const; void Remove(); void Update(); void DisplayDirectory() const; private: int maxSize, currentSize; Entry *entryList; void Grow(); // increase maximum size // return an index, given name int FindName(char *aName) const; };
26
directory.cpp #include <iostream> #include <cstring> #include “directory.h” using namespace std; Directory::Directory() { maxSize = 5; currentSize = 0; entryList = new Entry[maxSize]; } Directory::~Directory() { delete [] entryList; }
27
directory.cpp void Directory::Insert() { if (currentSize == maxSize) Grow(); entryList[currentSize++].Load(); }
28
directory.cpp void Directory::Lookup() const { char aName[20]; cout << “\tType the name to be looked up, followed by ENTER: “; cin.getline(aName, 20); int thisEntry = FindName(aName); if (thisEntry == -1) { cout << aName << “ not found\n”; } else { cout << “\nEntry found: “; entryList[thisEntry].Show(); }
29
directory.cpp void Directory::Remove() { char aName[20]; cout << “\nType name to be removed, followed by ENTER: “; cin.getline(aName, 20); int thisEntry = FindName(aName); if (thisEntry == -1) { cout << aName << “ not found”; } else { // shift entries down by one position for (int j = thisEntry +1; j < currentSize; j++) entryList[j – 1] = entryList[j]; currentSize--; cout << “Entry removed.\n”; }
30
directory.cpp void Directory::Update() { char aName[20]; cout << “\nPlease enter the name of the entry to be modified: “; cin.getline(aName, 20); int thisEntry = FindName(aName); if (thisEntry == -1) { cout << aName << “ not found”; } else { cout << “\nCurrent entry is: \n”; entryList[thisEntry].Show(); cout << “\n”Replace with new entries as follows: \n”; entryList[thisEntry].Load(); }
31
directory.cpp void Directory::DisplayDirectory() const { if (currentSize == 0) { cout << “\nCurrent directory is empty.\n”; return; } cout << “\n\t***NAME***\t\t***PHONE***\t\t***ADDRESS***\n\n”; for (int j = 0; j < currentSize; j++) { entryList[j].Show();
32
directory.cpp void Directory::Grow() { maxSize = currentSize + 5; Entry *newList = newEntry[maxSize]; for (int j = 0; j < currentSize; j++) newList[j] = entryList[j]; delete [] entryList; entryList = newList; } Int Directory::FindName(char *aName) const { if (strcmp(entryList[j].GetName(), aName) == 0) return j; return -1;
33
menu.cpp #include <cctype> // for toupper #include <iostream> #include “directory.h” using namespace std; void ShowMenu() { cout << "\n\t\t*** PIP 6 PHONE DIRECTORY ***"; cout << "\n\tI \tInsert a new entry into the directory"; cout << "\n\tL \tLook up an entry"; cout << "\n\tR \tRemove an entry"; cout << "\n\tU \tUpdate an entry"; cout << "\n\tD \tDisplay the entire directory"; cout << "\n\t? \tDisplay this menu"; cout << "\n\tQ \tQuit"; }
34
menu.cpp char GetAChar(const char *promptString) { char response; cout << promptString; cin >> response; response = topper(response); cin.get(); return response; } char Legal(char c) { return ((c == 'I') || (c == 'L') || (c == 'R') || (c == 'U') || (c == 'D') || (c == '?') || (c == 'Q'));
35
menu.cpp char GetCommand() { char cmd = GetChar(“\n\n>”); while (!Legal(cmd)) { cout << “\nIllegal command, please try again…”; ShowMenu(); cmd = GetAChar(“\n\n>”); } return cmd;
36
menu.cpp int main() { Directory d; ShowMenu(); char command; do { command = GetCommand(); switch(command) { case ‘I’: d.Insert(); break; case ‘L’: d.Lookup(); break; case ‘R’: d.Remove(); break; case ‘D’: d.DisplayDirectory(); break; case ‘?’: ShowMenu(); } } while (command != ‘Q’); return 0;
37
Bad Example stack (high address) int main() { … main
heap (low address)
38
Bad Example stack (high address) int main() { Directory d; … main
int maxSize = 5 int curentSize = 0 Entry *entryList = d Entry[5] heap (low address)
39
Bad Example stack (high address)
int main() { Directory d; d.Insert(); // 5 times … main int maxSize = 5 int curentSize = 5 Entry *entryList = d Entry[5] heap (low address)
40
Bad Example stack (high address)
int main() { Directory d; d.Insert(); // 5 times … d.GrowBad(); void Directory::GrowBad() { main int maxSize = 5 int curentSize = 5 Entry *entryList = d GrowBad() this = Entry[5] heap (low address)
41
Bad Example stack (high address)
int main() { Directory d; d.Insert(); // 5 times … d.GrowBad(); void Directory::GrowBad() { maxSize = currentSize + 5; main int maxSize = 10 int curentSize = 5 Entry *entryList = d GrowBad() this = Entry[5] heap (low address)
42
Bad Example stack (high address)
int main() { Directory d; d.Insert(); // 5 times … d.GrowBad(); void Directory::GrowBad() { maxSize = currentSize + 5; Entry newList[maxSize]; main int maxSize = 10 int curentSize = 5 Entry *entryList = d GrowBad() this = Entry newList[10] Entry[5] heap (low address)
43
Bad Example stack (high address)
int main() { Directory d; d.Insert(); // 5 times … d.GrowBad(); void Directory::GrowBad() { maxSize = currentSize + 5; Entry newList[maxSize]; for (int j = 0; j < currentSize; j++) newList[j] = entryList[j]; main int maxSize = 10 int curentSize = 5 Entry *entryList = d GrowBad() this = Entry newList[10] Entry[5] heap (low address)
44
Bad Example stack (high address)
int main() { Directory d; d.Insert(); // 5 times … d.GrowBad(); void Directory::GrowBad() { maxSize = currentSize + 5; Entry newList[maxSize]; for (int j = 0; j < currentSize; j++) newList[j] = entryList[j]; delete [] entryList; main int maxSize = 10 int curentSize = 5 Entry *entryList = d GrowBad() this = Entry newList[10] heap (low address)
45
Bad Example stack (high address)
int main() { Directory d; d.Insert(); // 5 times … d.GrowBad(); void Directory::GrowBad() { maxSize = currentSize + 5; Entry newList[maxSize]; for (int j = 0; j < currentSize; j++) newList[j] = entryList[j]; delete [] entryList; entryList = newList; } main int maxSize = 10 int curentSize = 5 Entry *entryList = d GrowBad() this = Entry newList[10] heap (low address)
46
Bad Example stack (high address)
int main() { Directory d; d.Insert(); // 5 times … d.GrowBad(); main int maxSize = 10 int curentSize = 5 Entry *entryList = d heap (low address)
47
Visualize the Execution
stack (high address) int main() { … main heap (low address)
48
Good Example stack (high address) int main() { Directory d; … main
int maxSize = 5 int curentSize = 0 Entry *entryList = d Entry[5] heap (low address)
49
Visualize the Execution
stack (high address) int main() { Directory d; d.Insert(); // 5 times … main int maxSize = 5 int curentSize = 5 Entry *entryList = d Entry[5] heap (low address)
50
Visualize the Execution
stack (high address) int main() { Directory d; d.Insert(); // 5 times … d.Grow(); void Directory::Grow() { main int maxSize = 5 int curentSize = 5 Entry *entryList = d Grow() this = Entry[5] heap (low address)
51
Visualize the Execution
stack (high address) int main() { Directory d; d.Insert(); // 5 times … d.Grow(); void Directory::Grow() { maxSize = currentSize + 5; main int maxSize = 10 int curentSize = 5 Entry *entryList = d Grow() this = Entry[5] heap (low address)
52
Visualize the Execution
stack (high address) int main() { Directory d; d.Insert(); // 5 times … d.Grow(); void Directory::Grow() { maxSize = currentSize + 5; Entry *newList = newEntry[maxSize]; main int maxSize = 10 int curentSize = 5 Entry *entryList = d Grow() this = Entry *newList = Entry[10] Entry[5] heap (low address)
53
Visualize the Execution
stack (high address) int main() { Directory d; d.Insert(); // 5 times … d.Grow(); void Directory::Grow() { maxSize = currentSize + 5; Entry *newList = newEntry[maxSize]; for (int j = 0; j < currentSize; j++) newList[j] = entryList[j]; main int maxSize = 10 int curentSize = 5 Entry *entryList = d Grow() this = Entry *newList = Entry[10] Entry[5] heap (low address)
54
Visualize the Execution
stack (high address) int main() { Directory d; d.Insert(); // 5 times … d.Grow(); void Directory::Grow() { maxSize = currentSize + 5; Entry *newList = newEntry[maxSize]; for (int j = 0; j < currentSize; j++) newList[j] = entryList[j]; delete [] entryList; main int maxSize = 10 int curentSize = 5 Entry *entryList = d Grow() this = Entry *newList = Entry[10] heap (low address)
55
Visualize the Execution
stack (high address) int main() { Directory d; d.Insert(); // 5 times … d.Grow(); void Directory::Grow() { maxSize = currentSize + 5; Entry *newList = newEntry[maxSize]; for (int j = 0; j < currentSize; j++) newList[j] = entryList[j]; delete [] entryList; entryList = newList; } main int maxSize = 10 int curentSize = 5 Entry *entryList = d Grow() this = Entry *newList = Entry[10] heap (low address)
56
Visualize the Execution
stack (high address) int main() { Directory d; d.Insert(); // 5 times … d.Grow(); main int maxSize = 10 int curentSize = 5 Entry *entryList = d Entry[10] heap (low address)
57
Note The destructor in Directory is responsible to free up the dynamically allocated Entry array When an object is automatically deallocated, only the space occupied by the object is deallocated Dynamically allocated memory is outside of the object, pointed by the pointer Need to be explicitly deallocated
58
Another Phonebook Example
Uses operator<< and >> instead of Show() and Load()
Similar presentations
© 2024 SlidePlayer.com. Inc.
All rights reserved.