Data Structure and Algorithm Design
Chapter 3
Xingang (Ian) Fang
Sections
Memory management
Memory management in classes
Memory Management for C++
Xingang (Ian) Fang
Outline
Overview
Memory Regions
Stack
Heap
Tools
Overview
Definition: Memory management in C++ refers to the process of allocating and deallocating memory for objects and data structures during the execution of a C++ program.
Motivation
Optimizing memory usage
Avoiding memory leaks
Avoiding dangling pointers
Avoiding stack overflow
Memory Regions
Four regions
Code: compiled program code.
Static: global and static variables
Stack: local variables and function parameters
Heap: dynamically allocated memory
Memory layout
Code and static regions are fixed size and stored in the lower memory addresses.
Stack and heap regions are variable size and stored in the higher memory addresses.
Stack and heap regions grow towards each other to share free space.
Stack Memory Management
Stack is a LIFO (Last In First Out) data structure/ADT.
The run-time maintains a stack data structure to store local variables and function parameters for function calls.
Automatically managed. A.k.a. automatic memory.
Stack overflow: when the stack is full and cannot grow further.
Caused by infinite recursion or allocating too much memory on the stack.
Further discussed in the recursion lecture.
Heap Memory Management
Heap is a dynamic data structure/ADT.
Managed by the programmer manually.
Allocate:
newoperatorDeallocate:
deleteoperator
A.k.a. dynamic memory.
Variables allocated on the heap are called dynamic data.
A pointer variable is the only way to access dynamic data. The pointer is a simple variable that stores the memory address of the dynamic data.
More on Dynamic Data
Always remember that the pointer is a separate variable from the dynamic data.
The
deleteoperator can take null pointers as arguments. It does nothing but will not cause any problems.It is a waste of time to add if like
if (ptr != nullptr) delete ptr;
Dynamic variables
MyClass *p = new MyClass();delete p;
Dynamic arrays
Include
[]around the operators.int *arr = new int[10];delete[] arr;
Complicated Dynamic Data (FYI)
2D Dynamic array
int **arr = new int*[10]; for (int i = 0; i < 10; ++i) arr[i] = new int[10]; // Use the array for (int i = 0; i < 10; ++i) delete[] arr[i]; delete[] arr;Local array of dynamic arrays as a 2D array
int *arr[10]; for (int i = 0; i < 10; ++i) arr[i] = new int[10]; // Use the array for (int i = 0; i < 10; ++i) delete[] arr[i];
Local array of object pointers
MyClass *myArr[10]; for (int i = 0; i < 10; ++i) myArr[i] = nullptr; int index = 0; while (true) { int value; cout << "Enter a value (negative value to exit): "; cin >> value; ++index; if (value < 0 || index >= 10) break; myArr[index] = new MyClass(value); } for (int i = 0; i < 10; ++i) delete myArr[i];Dynamic array of object pointers
MyClass **myArr = new MyClass*[10]; for (int i = 0; i < 10; ++i) myArr[i] = nullptr; int index= 0; while (true) { int value; cout << "Enter a value (negative value to exit): "; cin >> value; if (value < 0 || index >= 10) break; myArr[index] = new MyClass(value); } for (int i = 0; i < 10; ++i) delete myArr[i]; delete[] myArr;
Pitfalls
Memory leak: Forgetting to deallocate memory
Dangling pointer
Deallocating memory but still using the pointer
Double free
Deallocating memory twice
Memory access violation: when the program tries to access memory that it does not own. A.k.a. segmentation fault.
Invalid pointer
Null pointer
Uninitialized pointer
Assignment of a wrong type (e.g. int) to a pointer
Dangling pointer
Out-of-bound array access
Accessing an array element with an out-of-bound index
Accessing an array element with a negative index
Memory Management Tools
valgrindis a memory management tool for Linux.valgrindcan detect most memory management problems.Unfortunately not natively available on Mac OS.
Examples:
valgrind --tool=memcheck --leak-check=yes ./mainvalgrind --tool=memcheck --leak-check=yes --show-leak-kinds=all ./mainvalgrind --tool=memcheck --leak-check=yes --show-leak-kinds=all --track-origins=yes ./main
Read the valgrind memory check manual for more information.
Memory Management in Class Context for C++
Xingang (Ian) Fang
Outline
Overview
Memory Management in Class Context
Ownership of the dynamic data (FYI)
Overview
In the class context, because the flow of the program is not linear, the memory management is more complicated than in the function context.
E.g. For a class that owns a dynamic int array:
Declare an instance variable
int *array;Allocation in the constructor or other methods
this->array = new int[size];Deallocation in the destructor, etc.
delete[] this->array;Deep copy in the copy constructor, copy assignment operator, etc.
for (int i = 0; i < size; ++i) this->array[i] = other.array[i];
The ownership of the dynamic data is also more complicated. (FYI)
Memory Management in Class Context
Implicitly defined methods
Common mem problems in classes
The rule of three
-
shallow-copy.cpp: class with dynamic data and shallow copy
rule-of-3.cpp: improved version with the big three
Implicitly Defined Methods
The compiler will implicitly define the following methods if they are not explicitly defined by the programmer. They are called implicitly defined methods.
Default constructor: do nothing, potentially uninitialized variables
Copy constructor: straight copy by direct assignment, potentially shallow copy
Copy assignment operator overloading: straight copy by direct assignment, potentially shallow copy
Destructor: doing nothing
They can be used without explicitly defining them.
They will cause problem only when your class owns dynamic data and they are triggered.
You can totally rely on the implicit ones if your class does not own dynamic data.
You can explicitly define them to avoid the problems.
You can omit the ones that are not triggered even if your class owns dynamic data. Must be careful!
How to trigger
Default constructor
The constructor without any parameter
MyClass myObj();MyClass *obj = new MyClass();MyClass obj[10];You can always omit
()after the class name when they are empty.
Copy constructor
The constructor with one parameter of the same type as the class
MyClass myObj(otherObj);orMyClass myObj = otherObj;MyClass *obj = new MyClass(otherObj);Parameter passing by value
void func(MyClass obj)
Copy assignment operator overloading
MyClass obj1, obj2(10);and thenobj1 = obj2;
Destructor
When the local object is out of scope
MyClass *obj = new MyClass(); delete obj;
Common Memory Management Problems in Class Context
Memory leak
forget to deallocate the dynamic data in the destructor
Shallow copy
use a
=operator to copy dynamic datatriggered the implicitly defined copy constructor and copy assignment operator with dynamic data
May cause double free
Solution: Explicitly Defining These Methods
Precondition: your class owns dynamic data
Default constructor: initialize the pointer to
nullptror allocate memory. Or stay away and always use other constructorsCopy constructor: deep copy the dynamic data
Copy assignment operator overloading: deep copy the dynamic data
Destructor: deallocate the dynamic data
Known as the “The Rule of Three”
The Rule of Three
The “Rule of Three”
The Big Three: destructor, copy constructor, copy assignment operator overloading
Although they may not all be triggered, it is safer to define them all explicitly.
The “Rule of Three” is a rule (not a law)
Not enforced by the compiler.
Rather a guideline for memory security.
The “Rule of five” after C++11 for performance optimization (FYI)
Move constructor
Move assignment operator overloading
Example Class with Dynamic Data
Copy constructor:
Must take a const reference to class. Pass by value will trigger the copy constructor itself and cause problem.
Copy assignment operator overloading:
Must take a reference to the object too.
Must check self-assignment.
May need to deallocate the existing dynamic data before allocating new memory and copying the data.
Must return
*thisto allow chained assignment, if the return type is a reference to the class.
Ownership of the Dynamic Data (FYI)
Knowing who owns the dynamic data is important for memory management.
The class may:
Own the dynamic data.
implement the big three.
Own a pointer to the external dynamic data
treat the pointer as a regular variable
let the external owner to manage the dynamic data