Dynamic memory is allocated on the "heap". The heap is the region of computer memory which is managed by the programmer using pointers to access the memory.
Heap memory details:- can be accessed globally
- your limit is the physical memory limit of your system (real and virtual)
- user managed memory. Programmer must allocate and free memory.
- memory access is via pointer and thus requires dereferencing to access, requiring extra operations and time
- memory can become defragmented as memory is allocated and freed
- memory can be resized (eg using realloc())
- the "stack" grows and shrinks as functions push and pop local variables
- the "stack" is managed by the OS
- the default "stack" size is limited by the OS. A stack overflow occurs when this limit is exceeded (eg endless function recursion).
#include <iostream> using namespace std; class AAA { public: AAA(); AAA(int); ~AAA(); private: int ii; }; AAA::AAA() : ii(1) { cout << "Constructor called. Value of ii=" << ii << endl; } AAA::AAA(int _ii) : ii(_ii) { cout << "Constructor called. Value of ii=" << ii << endl; } AAA::~AAA() { cout << "Destructor called. Value of ii=" << ii << endl; } main() { cout << "Start of program" << endl; AAA a1; // Declared on the stack. AAA a2(2); // Declared on the stack. AAA *a3 = new AAA(3); // Dynamically allocated memory on the heap AAA *a4 = new AAA(4); // Dynamically allocated memory on the heap delete a3; // If not called, then destructor not called cout << "End of program" << endl; }
Run: ./a.out
Start of program Constructor called. Value of ii=1 Constructor called. Value of ii=2 Constructor called. Value of ii=3 Constructor called. Value of ii=4 Destructor called. Value of ii=3 End of program Destructor called. Value of ii=2 Destructor called. Value of ii=1
Note:
- Stack: The objects a1 and a2 are declared on the stack. The constructors are called automatically where they are declared. The destructors are called automatically when they go out of scope (as defined by the last "}").
- Heap: Objects allocated dynamically on the heap with "new" automatically have their constructors called. Note that the destructor for a3 is called when the memory is freed with "delete". Delete is not called for a4 and thus the destructor is not called.
C malloc() and free() do not call constructors or destructors. C++ new and delete operators are "class aware" and will call class constructors when a class is allocated with new and a destructor when delete is called. [example]
Mixing malloc with delete or new with free is undefined. In addition, C++ new and delete operators were introduced with the C++ practice of allocating memory in constructors and deallocating the same memory with a destructor.
new/delete advantages:
- new/delete invokes constructor/destructor. Malloc/free will not.
- new does not need typcasting. Malloc requires typcasting the returned pointer.
- new/delete operators can be overloaded, malloc/free can not.
- new does not require you to explicitly calculate the quantity of memory required. (Unlike malloc)
Use "malloc", "calloc" and "free": File: MallocTest.cpp
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <error.h> #include <errno.h> typedef struct { int ii; double dd; } SSS; int main() { int kk, jj; char *str1 = (char *) "This is a text string"; char *str2 = (char *) malloc(strlen(str1)); errno = 0; SSS *s1 = (SSS *)calloc(4, sizeof(SSS)); // Memory for 4 of struct SSS if(s1 == NULL) { if(errno == ENOMEM) printf("Error ENOMEM: Insufficient memory available\n"); } strcpy(str2,str1); /* Make a copy of the string */ for(kk=0; kk < 5; kk++) { s1[kk].ii=kk; } for(jj=0; jj < 5; jj++) { printf("Value stored: %d\n",s1[jj].ii); } free(s1); free(str2); }
Compile: gcc -o MallocTest MallocTest.cpp
Run: MallocTest
Value stored: 0 Value stored: 1 Value stored: 2 Value stored: 3 Value stored: 4
Manual pages:
- malloc: a memory allocator
- calloc: a memory allocator
- realloc: memory reallocator
- free: frees the memory space pointed to by ptr
- errno: obtains error number of last error
- perror: obtains error number of last error
Use "new" and "delete": File: AllocNewTest.cpp
#include <iostream> class CCC { public: CCC(){}; CCC(int); CCC(int, double); int ii; double dd; }; CCC::CCC(int _ii) : ii(_ii) { }; CCC::CCC(int _ii, double _dd) : ii(_ii), dd(_dd) { }; using namespace std; main() { CCC *cc1 = new CCC(4, 5.5); // Pointer. Contructor called. CCC *cc2 = new CCC[5]; // Pointer to an array of objects. CCC &cc3 = *new CCC; // Reference CCC **c4 = new CCC * [5]; // Array of pointers to pointers cc1->ii = 5; cc2[3].ii = 6; cc3.ii = 7; c4[0] = new CCC(8); c4[1] = new CCC(9); cout << cc1->ii << endl; cout << cc2[3].ii << endl; cout << cc3.ii << endl; cout << c4[0]->ii << endl; cout << c4[1]->ii << endl; delete cc1; delete [] cc2; delete & cc3; delete [] c4[0]; // First delete pointer content delete [] c4[1]; delete [] c4; // then delete array of pointers }
Note the difference between:
- new CCC(3) creates memory for a single object who's integer member is set to 3.
- new CCC[3] creates memory for three objects of type CCC and no variables are set.
Compile: g++ -o AllocNewTest AllocNewTest.cpp
Run: ./a.out
5 6 7 8 9
C function returning a pointer:
#include <stdio.h> #include <stdlib.h> char *fnConvert(int _ii) { char *str = (char *) malloc(10); /* Return 10 character string */ if(str == NULL) { fprintf(stderr,"Error: Memory allocation failed.\n"); perror("malloc"); } sprintf(str, "%d", _ii); return str; } main() { char *s1=fnConvert( 34567 ); printf("%s\n", s1); free(s1); }
C++ function returning a copy:
#include <iostream> #include <sstream> #include <string> using namespace std; string fnConvert(int _ii) { ostringstream ost; ost << _ii; return ost.str(); } main() { cout << fnConvert( 34567 ) << endl; }
Note: The STL string class copy constructor is employed to return a copy (return by value). Yes the variable "ost" is out of scope once we leave the function, but the copy of its contents is valid. Do not "return ost.str().c_str()" as this pointer is out of scope once the procesing leaves the function and the data lost.
Use exception handling:
#include <iostream> using namespace std; main() { int ii; double *ptr[5000000]; try { for( ii=0; ii < 5000000; ii++) { ptr[ii] = new double[5000000]; } } catch ( bad_alloc &memmoryAllocationException ) { cout << "Error on loop number: " << ii << endl; cout << "Memory allocation exception occurred: " << memmoryAllocationException.what() << endl; } catch(...) } cout << "Unrecognized exception" << endl; { }
Compile: g++ -o AllocNewTest AllocNewTest.cpp
Run:
- Observer system limits: ulimit -a
- Set system limits: ulimit -m 100
- Run with fewer privileges: nice -n 19 AllocNewTest
Using polymorphism to delete dynamically allocated objects by their base class pointer: Declare base class destructor virtual so that the delete operator can be applied to the base class pointer. The derived class destructor is called first, before the base class destructor.
#include <iostream> using namespace std; class Base { public: Base(){}; virtual ~Base(){ cout << "Base class destructor called" << endl; } }; class Derived : public Base { public: Derived(){}; ~Derived(){ cout << "Derived class destructor called" << endl; } }; main() { Base *ptr = new Derived(); delete ptr; }
Resutls:
Derived class destructor called
Base class destructor called
Note:
- If the delete operator is applied to the base class and the destructor is NOT virtual, then this will cause a memory leak as only a portion of the memory is freed.
- Base class destructor is not pure virtual (set =0) or there would be no base class implementation of the destructor..
- Class contructors can NOT be virtual.