Memory Management In C++


Published on

This is one of my first presentations on Advanced C++ stuff

Published in: Technology, Business
  • Be the first to comment

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

Memory Management In C++

  1. 1. Memory Management in C++ Advanced techniques and idioms -ShriKant Vashishtha
  2. 2. <ul><li>Distinct memory areas in C++ </li></ul><ul><li>Const Data </li></ul><ul><li>Stack </li></ul><ul><li>Free Store </li></ul><ul><li>Heap </li></ul><ul><li>Global/Static </li></ul>
  3. 3. Distinct Memory areas in C++ <ul><li>Const Data </li></ul><ul><li>Stores string literals and other data whose values are known at compile time </li></ul><ul><li>No objects of class type </li></ul><ul><li>Available during entire lifetime of the program </li></ul><ul><li>read-only data? </li></ul>
  4. 4. Distinct Memory areas in C++ <ul><li>Stack </li></ul><ul><li>Stores automatic variables </li></ul><ul><li>Allocation is much faster than dynamic memory allocation - why? </li></ul><ul><li>Objects are constructed immediately after memory is allocated and destroyed before memory is deallocated. </li></ul>
  5. 5. Distinct Memory areas in C++ <ul><li>Free Store </li></ul><ul><li>One of dynamic memory areas. </li></ul><ul><li>Allocated/freed by new/delete. </li></ul><ul><li>Objects are constructed after memory is allocated and are destroyed before memory is deallocated…then what? </li></ul><ul><li>What about Object manipulation at the time of memory allocation? </li></ul>
  6. 6. Distinct Memory areas in C++ <ul><li>Heap </li></ul><ul><li>Allocated/freed by malloc/free and their variants. </li></ul><ul><li>Not same as free store…why? </li></ul><ul><li>For class types the memory can be allocated and deallocated by placement-new and explicit destruction. </li></ul>
  7. 7. Distinct Memory areas in C++ <ul><li>Global/Static </li></ul><ul><li>Storage allocated at program startup. </li></ul><ul><li>May not be initialized until after program has started execution. </li></ul><ul><li>The order of initialization of global variables across translation units is not defined. </li></ul><ul><li>Object access rules as are of Free Store. </li></ul>
  8. 8. Pointers Vs References <ul><li>Both do similar things. </li></ul><ul><li>There is no such thing as a null reference. Must always refer to some object. </li></ul><ul><li>Pointers can be null. </li></ul><ul><li>What about this.. </li></ul><ul><li> char * pc=0; //set pointer to null </li></ul><ul><li>char & rc = *pc; //make reference refer to dereferenced null pointer </li></ul>
  9. 9. Pointers Vs References <ul><li>Reference must initialized. </li></ul><ul><li>String & rs; //error </li></ul><ul><li>string s(“xyz”); </li></ul><ul><li>string & rs = s; //OK. rs refers to s; </li></ul><ul><li>No such restriction for pointers </li></ul><ul><li>string * ps; //uninitialized pointer </li></ul><ul><li> //valid but risky. </li></ul>
  10. 10. Pointers Vs References <ul><li>Pointers may be reassigned to refer to different objects. A reference always refers to the object with which it is initialized. </li></ul><ul><li>string s1(“Ram”); </li></ul><ul><li>string s2(“Krishna”); </li></ul><ul><li>string & rs = s1; </li></ul><ul><li>string * ps = &s1; //rs still refers to s1 but s1’s value is </li></ul><ul><li>//now “Krishna” </li></ul><ul><li>rs=s2; </li></ul><ul><li>ps = &s2; //ps now points to s2. s1 is unchanged. </li></ul>
  11. 11. Conclusions: Pointers Vs References <ul><li>References are more efficient. </li></ul><ul><li>void func (const string & s){ </li></ul><ul><li>cout << s; </li></ul><ul><li>} </li></ul><ul><li>Vs. </li></ul><ul><li>void func2(const string * ps){ </li></ul><ul><li>if(ps){ </li></ul><ul><li>cout << *ps; </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
  12. 12. Conclusions: Pointers Vs References <ul><li>Use a pointer whenever we need to take into account the possibility that there’s nothing to refer to or whenever we need to be able to refer to different things at different times. </li></ul><ul><li>References can be used when we know that there is something to refer to and we know that we’ll never want to refer to something else. </li></ul>
  13. 13. Conclusions: Pointers Vs References <ul><li>Use references while implementing operators whose syntactic requirements make the use of pointers undesirable. </li></ul><ul><li>vector<int> v(10); </li></ul><ul><li>v[5] = 10; //the target of this assignment </li></ul><ul><li> //is the return value of operator [] </li></ul><ul><li>Vs </li></ul><ul><li>*v[5] = 10; </li></ul>
  14. 14. ‘new’ operator <ul><li>string * ps = new string(“sample”); </li></ul><ul><li>new operator does two fold work. Allocates memory to hold an object of the ‘string’ type. Calls a constructor to initialize an object in the memory allocated. </li></ul><ul><li>This semantics cannot be changed. </li></ul><ul><li>What we can change is how the memory for an object is allocated. How? </li></ul>
  15. 15. ‘ new’ operator Vs operator new <ul><li>new operator calls a function to allocate memory and we can rewrite or overload(????) that function to change its behavior. The name of the function is operator new. </li></ul><ul><li>void * operator new(size_t size); </li></ul><ul><li>void * rawmemory = operator new(sizeof(string)); </li></ul><ul><li>We can overload operator new by adding additional parameters, but the first parameter must always be of type size_t. </li></ul>
  16. 16. ‘ new’ operator Vs operator new <ul><li>string * ps = new string(“sample”); </li></ul><ul><li>more or less corresponds to </li></ul><ul><li>void * memory = operator new(sizeof(string)); </li></ul><ul><li>call string::string(“sample”) on *memory; </li></ul><ul><li>string *ps = static_cast<string *>(memory); </li></ul><ul><li>Second step involves calling constructor which programmer is prohibited from doing. Why? </li></ul>
  17. 17. ‘ new’ operator Vs operator new <ul><li>Constructor involves necessary steps to initialize the object (including vtbl initialization). </li></ul>
  18. 18. Placement new <ul><li>A special version of operator new. </li></ul><ul><li>There are times when we really want to call a constructor directly. When?? </li></ul><ul><li>When we have some raw memory that’s already been allocated and we need to construct an object in the memory we have. </li></ul>
  19. 19. Placement new <ul><li>ClassA{ </li></ul><ul><li>public: </li></ul><ul><li>ClassA(int a); </li></ul><ul><li>… </li></ul><ul><li>}; </li></ul><ul><li>ClassA * getClassAInBuff(void * buffer, int a){ </li></ul><ul><li>return new(buffer)ClassA(a); </li></ul><ul><li>} </li></ul><ul><li>This operator new is placement new and looks like this: </li></ul><ul><li>void * operator new(size_t, void * location){ </li></ul><ul><li>return location; </li></ul><ul><li>} </li></ul>
  20. 20. Summary: new operator Vs operator new Vs placement new <ul><li>To create an object on free store, use the new operator. </li></ul><ul><li>To allocate memory only, call operator new. No constructor will be called. </li></ul><ul><li>For customization of memory allocation write your on operator new and use the new operator. </li></ul><ul><li>To construct an object in memory we have already got a pointer to, use placement new. </li></ul>
  21. 21. The C++ Object Model <ul><li>Class Point{ </li></ul><ul><li>public: </li></ul><ul><li>Point(float val); </li></ul><ul><li>virtual ~Point(); </li></ul><ul><li>float x() const; </li></ul><ul><li>static int PointCount(); </li></ul><ul><li>protected: </li></ul><ul><li>virtual ostream & print(ostream & os)const; </li></ul><ul><li>float _x; </li></ul><ul><li>static int _point_count; </li></ul><ul><li>}; </li></ul>Float_x ---------------------- __vptr__Point --------------------- --------------------- Point::~Point() Point::print(ostream &) static int Point:: _point_count static int Point::PointCount() Point pt; Virtual table for Point type_info for Point Point:: Point(float) Float Point::x()
  22. 22. The C++ Object Model <ul><li>Nonstatic data members are allocated directly within each class object. </li></ul><ul><li>Static data members are stored outside the individual class object. </li></ul><ul><li>Static and nonstatic member functions are also hoisted outside the class object. </li></ul><ul><li>Virtual functions </li></ul><ul><ul><li>A table of pointers to virtual functions is generated for each class object (called virtual table). </li></ul></ul><ul><ul><li>Single pointer to the associated virtual table inserted in each class object. Handled by class constructor, destructor and copy assignment operator. </li></ul></ul><ul><ul><li>The type_info object associated with each class in support of RTTI is also addressed in virtual table. </li></ul></ul>
  23. 23. C++ Object Model: Evaluation <ul><li>Strength is space and runtime efficiency. </li></ul><ul><li>Drawback is the need to recompile unmodified code that makes use of an object of a class for which there has been any modification of the nonstatic data members. </li></ul>
  24. 24. C++ Object Model: Inheritance <ul><li>class Animal{ … }; </li></ul><ul><li>class Bird : public Animal{ … }; </li></ul><ul><li>class Parrot:public Bird{ … }; </li></ul><ul><li>class iostream: public istream, public ostream { …}; </li></ul><ul><li>class istream : virtual public ios { … }; </li></ul><ul><li>class ostream : virtual public ios { … }; </li></ul><ul><li>The data members of the base class subobject are directly stored in within the derived class object. </li></ul><ul><li>For virtual inheritance only a single instance of the base class is maintained irrespective of how many times it is derived. </li></ul><ul><li>The support for virtual base class adds a pointer into the derived class. </li></ul>The most compact and the most efficient access of base class data members Drawback: Any change to data members requires recompiling
  25. 25. C++ Object Model: Object Mechanics <ul><li>X foobar(){ </li></ul><ul><li>X xx; </li></ul><ul><li>X *px = new X; </li></ul><ul><li>//foo is a virtual //function </li></ul><ul><li>; </li></ul><ul><li>px->foo(); </li></ul><ul><li>delete px; </li></ul><ul><li>return xx; </li></ul><ul><li>} </li></ul><ul><li>void foobar(X & _result){ </li></ul><ul><li>_result.X::X(); </li></ul><ul><li>px = _new(sizeof(X)); </li></ul><ul><li>if(px) </li></ul><ul><li>px->X::X(); </li></ul><ul><li>foo(&_result); </li></ul><ul><li>{ *px->_vtbl[2])(px); </li></ul><ul><li>if(px != 0){ </li></ul><ul><li>(*px->vtbl[1]) (px); //destructor </li></ul><ul><li>_delete(_px); </li></ul><ul><li>} </li></ul><ul><li>return; </li></ul><ul><li>}; </li></ul>expand X * px = new X 1. construct _result 2. result replaces local xx expand suppress virtual mechanics expand px->foo() using virtual mechanics expand delete px;
  26. 26. The type of a Pointer <ul><li>ZooAnimal *px; </li></ul><ul><li>int * pi; </li></ul><ul><li>Array< string > *pta; </li></ul><ul><li>What’s the difference between these three pointers? </li></ul><ul><li>What address space does a void * pointer holds memory location 1000 span??? </li></ul><ul><li>There is no difference in terms of memory requirements. All they need is a machine address to get allocated (which is 4 bytes normally). </li></ul><ul><li>No difference in terms of representation of the pointer nor in addresses the pointers may hold. </li></ul><ul><li>The difference lies in the type of the object being addressed. </li></ul><ul><li>Type of a pointer instructs the compiler as to how to interpret the memory found at a particular address and also just how much memory that interpretation should span. </li></ul>Can only hold an address and not actually operate on the object it addresses
  27. 27. Object deletion and Memory deallocation <ul><li>To avoid resource leaks, every dynamic allocation must be matched by equal and opposite deallocation. </li></ul><ul><li>String * ps = new string(); </li></ul><ul><li>.. </li></ul><ul><li>delete ps; </li></ul><ul><li>corresponds to </li></ul><ul><li>ps->~string(); //calls the object’s destructor </li></ul><ul><li>operator delete(ps); //deallocate the memory the object occupied. </li></ul>
  28. 28. Object deletion and Memory deallocation <ul><li>If placement new operator is used to create an object in some memory, we should avoid using delete operator to deallocate the memory. Why?? </li></ul>
  29. 29. Object deletion and Memory deallocation <ul><li>void * mallocshared(size_t size); </li></ul><ul><li>void freeshared(void * memory); </li></ul><ul><li>void * memory = mallocShared(sizeof(ClassA)); </li></ul><ul><li>ClassA * pa = new(memory) ClassA(2); </li></ul><ul><li>delete pa; //undefined!!!! Sharedmemory came </li></ul><ul><li> //from mallocShared not operator new </li></ul><ul><li>pa->~ClassA();//fine </li></ul><ul><li>freeShared(pa);//fine </li></ul>
  30. 30. operator delete function <ul><li>o perator delete(ps); </li></ul><ul><li>void operator delete(void * memoryToBeDeallocated); </li></ul><ul><li>operator delete to delete operator is analogous to operator new for new operator. </li></ul><ul><li>The memory deallocation allocated by operator new is performed by the operator delete function. </li></ul>
  31. 31. C++ equivalent of malloc-free <ul><li>void * buffer = operator new(50*sizeof(char)); </li></ul><ul><li>operator delete(buffer); </li></ul>
  32. 32. Arrays <ul><li>void * buffer = operator new(50*sizeof(char)); </li></ul><ul><li>operator delete(buffer); </li></ul>
  33. 33. Why overload new/delete operator? <ul><li>The new operator must have its equivalent corresponding delete operator in all cases. </li></ul>
  34. 34. Common memory problems <ul><li>A) Memory allocation conflict </li></ul><ul><li>1.char *a; </li></ul><ul><li>a = new char; </li></ul><ul><li>free(a); </li></ul><ul><li>2.char *a; </li></ul><ul><li>a = (char *) malloc(1); </li></ul><ul><li>delete a; </li></ul><ul><li>B) Copying pointer which is out-of-range </li></ul><ul><li>char *a, *b; </li></ul><ul><li>a = new char[10]; </li></ul><ul><li>b = a + 20; </li></ul>
  35. 35. Common memory problems <ul><li>C) Inconsistent usage of delete operator </li></ul><ul><li>1. int *a = new int [5]; </li></ul><ul><li> delete a; </li></ul><ul><li>2. int * a = new int; </li></ul><ul><li> delete [] a; </li></ul><ul><li>D) Writing to a dangling pointer </li></ul><ul><li> char *a = (char *)malloc(10); </li></ul><ul><li> free(a); </li></ul><ul><li> *a = 'x'; </li></ul><ul><li>E) Freeing memory block from body </li></ul><ul><li> char *a = (char *)malloc(10); </li></ul><ul><li> free(a+1); </li></ul>
  36. 36. Common memory problems <ul><li>C) Writing to a NULL pointer </li></ul><ul><li> int *a; </li></ul><ul><li> int main(){ </li></ul><ul><li> *a = 123; </li></ul><ul><li> return (0); </li></ul><ul><li> } </li></ul><ul><li>D) Writing overflows memory </li></ul><ul><li> int main(){ </li></ul><ul><li>int junk; </li></ul><ul><li>char a[10]; </li></ul><ul><li>strcpy(a, &quot;A simple test&quot;); </li></ul><ul><li>return (0); </li></ul><ul><li> } </li></ul>
  37. 37. Common memory problems <ul><li>F) Memory leaked leaving scope </li></ul><ul><li>#include <string.h> </li></ul><ul><li>union S1 { </li></ul><ul><li>char *cp; </li></ul><ul><li>S1() { cp=new char[10]; } </li></ul><ul><li>S1(char *p) { </li></ul><ul><li>cp=new char[10]; </li></ul><ul><li>strcpy(cp,p); </li></ul><ul><li>} </li></ul><ul><li>S1(S1 &s) { </li></ul><ul><li>cp=new char[10]; </li></ul><ul><li>strcpy(cp,s.cp); </li></ul><ul><li>} </li></ul><ul><li>void mf(char *p){ strcpy(cp,p);} </li></ul><ul><li>}; </li></ul><ul><li>void foo() { </li></ul><ul><li>S1 s1,s2(&quot;Hello &quot;),s3=s2; </li></ul><ul><li>;SADF&quot;); </li></ul><ul><li>;World&quot;); </li></ul><ul><li>} </li></ul><ul><li>int main() { </li></ul><ul><li>foo(); </li></ul><ul><li>return(0); </li></ul><ul><li>} </li></ul>
  38. 38. Common memory problems <ul><li>F) Reading overflows memory </li></ul><ul><li>1. int main() { </li></ul><ul><ul><li>char *a = &quot;TEST&quot;; </li></ul></ul><ul><ul><li>char b[20]; </li></ul></ul><ul><ul><li>memcpy(b, a, sizeof(b)); </li></ul></ul><ul><ul><li>return (0); </li></ul></ul><ul><ul><li>} </li></ul></ul><ul><li>2. int main(){ </li></ul><ul><ul><li>char junk; </li></ul></ul><ul><ul><li>char b[8]; </li></ul></ul><ul><ul><li>strncpy(b, &quot;This is a test&quot;, sizeof(b)); </li></ul></ul><ul><ul><li>printf(&quot;%s &quot;, b); </li></ul></ul><ul><ul><li>return (0); </li></ul></ul><ul><ul><li>} </li></ul></ul><ul><li>3. class small{ </li></ul><ul><ul><li>public: </li></ul></ul><ul><ul><li>int x; </li></ul></ul><ul><ul><li>}; </li></ul></ul><ul><ul><li>class big : public small{ </li></ul></ul><ul><ul><li>public: </li></ul></ul><ul><ul><li>double y; </li></ul></ul><ul><ul><li>}; </li></ul></ul><ul><ul><li>int main(){ </li></ul></ul><ul><ul><li>small *var1; </li></ul></ul><ul><ul><li>big *var2; </li></ul></ul><ul><ul><li>double d; </li></ul></ul><ul><ul><li>var1 = new small; </li></ul></ul><ul><ul><li>var2 = (big *) var1; </li></ul></ul><ul><ul><li>d = var2->y; </li></ul></ul><ul><ul><li>return 0; </li></ul></ul><ul><ul><li>} </li></ul></ul>
  39. 39. Problems with Pointer <ul><li>We really don’t like them much anyway. </li></ul><ul><li>Allocation on the free store has to be associated with delete operator wherever we exit from the scope normally or abnormally. </li></ul><ul><li>Double-deallocation/heap corrupting </li></ul><ul><li>Memory leaks </li></ul><ul><li>Dangling pointers </li></ul>
  40. 40. Solutions to the problems with Pointers
  41. 41. Use Destructors <ul><li>class ALA{ </li></ul><ul><li>public: </li></ul><ul><li>virtual void processAdoption()=0; </li></ul><ul><li>… </li></ul><ul><li>}; </li></ul><ul><li>class Puppy: public ALA{ </li></ul><ul><li>public: </li></ul><ul><li>virtual void processAdoption(); </li></ul><ul><li>... </li></ul><ul><li>}; </li></ul><ul><li>class Kitten:public ALA{ </li></ul><ul><li>public: </li></ul><ul><li>virtual void processAdoption(); </li></ul><ul><li>... </li></ul><ul><li>}; </li></ul><ul><li>ALA * readALA(istream &s); </li></ul><ul><li>void processAdoptions(istream & dataSource{ </li></ul><ul><li>while(dataSource){ </li></ul><ul><li>ALA *pa = readALA(dataSource); </li></ul><ul><li>pa->processAdoption(); </li></ul><ul><li>delete pa; </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>Read animal info from istream object and return a pointer to the object of appropriate ALA type Can lead to resource leak in exceptional conditions.
  42. 42. Use Destructors <ul><li>Option 1 </li></ul><ul><li>void processAdoptions(istream & dataSource){ </li></ul><ul><li>while(dataSource){ </li></ul><ul><li> ALA *pa = readALA(dataSource); </li></ul><ul><li> try{ </li></ul><ul><li> pa->processAdoption(); </li></ul><ul><li> } </li></ul><ul><li> catch(…){ </li></ul><ul><li> delete pa; </li></ul><ul><li> throw; </li></ul><ul><li> } </li></ul><ul><li> delete pa; </li></ul><ul><li>} </li></ul><ul><li>} </li></ul><ul><li>Problems </li></ul><ul><ul><li>Code is littered with try and catch blocks. </li></ul></ul><ul><ul><li>Duplicate cleanup. </li></ul></ul><ul><ul><li>In any case whether exceptional or by a normal return, we need to delete pa. </li></ul></ul><ul><ul><li>Solution can be to move cleanup code in the destructor of a local object of processAdoptions(). </li></ul></ul>
  43. 43. Use Destructors <ul><li>Option 1 </li></ul><ul><li>void processAdoptions(istream & dataSource){ </li></ul><ul><li>while(dataSource){ </li></ul><ul><li> auto_ptr<ALA> pa(readALA(dataSource)); </li></ul><ul><li> pa->processAdoption(); </li></ul><ul><li>} </li></ul><ul><li>} </li></ul><ul><li>template<class T> </li></ul><ul><li>class auto_ptr{ </li></ul><ul><li>public: </li></ul><ul><li>auto_ptr(T *p = 0):ptr(p) {} </li></ul><ul><li>~auto_ptr() { delete ptr; } </li></ul><ul><li>private: </li></ul><ul><li>T * ptr; </li></ul><ul><li>}; </li></ul><ul><li>Advantages </li></ul><ul><ul><li>Resource is deleted automatically whenever object goes out of scope. </li></ul></ul><ul><ul><li>No resource leaks in exceptional or normal conditions. </li></ul></ul><ul><li>Problems </li></ul><ul><ul><li>Cannot be applied for arrays. </li></ul></ul>Save ptr to object delete ptr to object
  44. 44. Smart Pointers(auto_ptr template) <ul><li>Option 1 </li></ul><ul><li>void processAdoptions(istream & dataSource){ </li></ul><ul><li>while(dataSource){ </li></ul><ul><li> auto_ptr<ALA> pa(readALA(dataSource)); </li></ul><ul><li> pa->processAdoption(); </li></ul><ul><li>} </li></ul><ul><li>} </li></ul><ul><li>template<class T> </li></ul><ul><li>class auto_ptr{ </li></ul><ul><li>public: </li></ul><ul><li>auto_ptr(T *p = 0):ptr(p) {} </li></ul><ul><li>~auto_ptr() { delete ptr; } </li></ul><ul><li>private: </li></ul><ul><li>T * ptr; </li></ul><ul><li>}; </li></ul><ul><li>Advantages </li></ul><ul><ul><li>Resource is deleted automatically whenever object goes out of scope. </li></ul></ul><ul><ul><li>No resource leaks in exceptional or normal conditions. </li></ul></ul><ul><li>Problems </li></ul><ul><ul><li>Cannot be applied for arrays. </li></ul></ul>Save ptr to object delete ptr to object