980 likes | 1.12k Views
Christian Schulte cschulte@kth.se Software and Computer Systems School of Information and Communication Technology KTH – Royal Institute of Technology Stockholm, Sweden. C++: Memory Management, Summary. ID1218 Lecture 10 2009-11-30. Overview. Memory management case studies
E N D
Christian Schulte cschulte@kth.se Software andComputer Systems School of Information and Communication Technology KTH – Royal Institute of Technology Stockholm, Sweden C++: Memory Management, Summary ID1218 Lecture 10 2009-11-30
Overview • Memory management case studies • resizable arrays • freelists • reference counting • Summary C and C++ • summary… • … and questions you should be able to answer ID1218, Christian Schulte
Memory Management Case studies ID1218, Christian Schulte
Memory Management • How to… • fit C++ structure, example: resizable arrays • make efficient, example: free lists • Who does what? • who allocates? • who is responsible for deallocation? • example: reference counting ID1218, Christian Schulte
Resizable Arrays ID1218, Christian Schulte
Resizable Arrays • Should behave like an array • the number of elements can be changed • provides correct construction, deletion, assignment • supports array access operator [] • can be passed as constant reference • polymorphic with respect to element type • can be mixed with "normal" arrays ID1218, Christian Schulte
Resizable Arrays • Members • current size: unsigned int • element array: *Element • First attempt for integers • make polymorphic in last step ID1218, Christian Schulte
Resizable Arrays: Class class IRA { private: unsigned int _n; int* _x; public: IRA(unsigned int n=8); IRA(const IRA& ira); IRA& operator=(const IRA& ira); void resize(unsigned int n); int get(unsigned inti) const; void set(unsigned inti, int x); ~IRA(); }; ID1218, Christian Schulte
Constructor class IRA { private: … public: IRA(unsigned int n=8) : _n(n), _x(new int[_n]) {} … }; • Any member function defined inside class is inline • Member functions can also be defined outside ID1218, Christian Schulte
Inline Definition class IRA { private: … public: IRA(unsigned int n=8); … }; inline IRA::IRA(unsigned int n) : _n(n), _x(new int[_n]) {} • Any member function defined inside class is inline • Member functions can also be defined outside ID1218, Christian Schulte
Outline Definition • Header file: class IRA { private: … public: IRA(unsigned int n=8); … }; • Implementation file IRA::IRA(unsigned int n) : _n(n), _x(new int[_n]) {} ID1218, Christian Schulte
Copy Constructor IRA::IRA(const IRA& ira) : _n(ira._n), _x(new int[_n]) { for (unsigned int i=0; i<_n; i++) _x[i] = ira._x[i]; } • Design: nothing shared between copies ID1218, Christian Schulte
Destructor IRA::~IRA() { delete [] _x; } • Design: nothing shared between copies ID1218, Christian Schulte
Assignment Operator IRA& IRA::operator=(const IRA& ira) { if (this != &ira) { delete [] _x; _n = ira._n; _x = new int[_n]; for (unsigned inti=0; i<_n; i++) _x[i] = ira._x[i]; } return *this; } • Design: nothing shared between copies ID1218, Christian Schulte
Access inline int IRA::get(unsigned inti) const { return _x[i]; } inline void IRA::set(unsigned inti, int x) { _x[i]=x; } • Must go into header file ID1218, Christian Schulte
Access: Assert Proper Use inline int IRA::get(unsigned inti) const { assert(i < _n); return _x[i]; } inline void IRA::set(unsigned inti, int x) { … } • Checked at runtime • requires #include <cassert> • runtime error, if violated (can be looked for in debugger) • ignored, if the macro NDEBUG is defined • pass –DNDEBUG from commandline ID1218, Christian Schulte
Resizing the Array void IRA::resize(unsigned int n) { int* x = new int[n]; unsigned int m = std::min(n,_n); for (unsigned inti=0; i<m; i++) x[i] = _x[i]; delete [] _x; _n = n; _x = x; } • std::min requires #include <algorithm> ID1218, Christian Schulte
Overloading []: First Attempt class IRA { private: unsigned int _n; int* _x; public: IRA(unsigned int n=8); IRA(const IRA& ira); IRA& operator=(const IRA& ira); void resize(unsigned int n); int operator[](unsigned inti); ~IRA(); }; ID1218, Christian Schulte
Overloading []: First Attempt inline int IRA::operator[](unsigned inti) { assert(i < n); return _x[i]; } • Does not work: ira[7] = 4; • must pass a reference instead • Does not work: ira[7] if ira is const IRA& • must also provide const version ID1218, Christian Schulte
Overloading []: Second Attempt class IRA { private: unsigned int _n; int* _x; public: IRA(unsigned int n=8); IRA(const IRA& ira); IRA& operator=(const IRA& ira); void resize(unsigned int n); int& operator[](unsigned inti); const int& operator[](unsigned inti) const; ~IRA(); }; ID1218, Christian Schulte
Overloading []: Second Attempt inline int& IRA::operator[](unsigned int i) { assert(i < n); return _x[i]; } • Works for: ira[7] = 4; • passes a reference to position in array ID1218, Christian Schulte
Overloading []: Second Attempt inline const int& IRA::operator[](unsigned int i) const { assert(i < n); return _x[i]; } • Works for ira[7] if ira is const IRA& • provides const version ID1218, Christian Schulte
Generic Resizable Arrays template <typename T> class RA { private: unsigned int _n; T* _x; public: RA(unsigned int n=8); RA(const RA<T>& ra); RA<T>& operator=(const RA<T>& ra); void resize(unsigned int n); T& operator[](unsigned inti); const T& operator[](unsigned inti) const; ~RA(); }; ID1218, Christian Schulte
Generic Copy Constructor template <typename T> RA<T>::RA(const RA<T>& ra) : _n(ra._n), _x(new T[_n]) { for (unsigned inti=0; i<_n; i++) _x[i] = ra._x[i]; } • Define in header file • What is executed: • default constructor? yes! • copy constructor? no! • assignment operator? yes! ID1218, Christian Schulte
Mixing With Normal Arrays RA<int> x(42); void f(int* a); void g(int a[]); f(x); g(x); • Both calls do not work: x is of type RA<int> • Simple solution: f(&x[0]); g(&x[0]); ID1218, Christian Schulte
Mixing With Normal Arrays template <typename T> class RA { … public: … operator T*(void) { return _x; } }; • Dangerous… RA<int> x(42); int* y = x; x.resize(1024); y[0] = 4; ID1218, Christian Schulte
Freelists ID1218, Christian Schulte
Efficient Lists • Common behavior • create and delete list cells very often • Memory allocation and deallocation is expensive • Idea • store allocated but unused list cells in a freelist • allocate from freelist whenever possible • deallocate to freelist ID1218, Christian Schulte
Integer Lists class IL { private: int _hd; IL* _tl; public: IL(int h, IL* t = NULL); inthd() const; void hd(int h); IL* tl() const; void tl(IL* t); }; • Basic required functionality • no default constructor, no … • Naïve memory policy: delete single list cell • refine, if different policy needed ID1218, Christian Schulte
Integer Lists inline IL::IL(int h, IL* t) : _hd(h), _tl(t) {} inline int IL::hd() const { return _hd; } inline void IL::hd(int h) { _hd=h; } inline IL* IL::tl() const { return _tl; } inline void IL::tl(IL* t) { _tl=t; } ID1218, Christian Schulte
Overloading Memory Allocation #include <cstdlib> class IL { private: int _hd; IL* _tl; public: IL(int h, IL* t = NULL); inthd() const; void hd(int h); IL* tl() const; void tl(IL* t); static void* operator new(size_t s); static void operator delete(void* p); }; ID1218, Christian Schulte
Overloading Memory Allocation inline void* IL::operator new(size_t s) { return malloc(s); } inline void IL::operator delete(void* p) { free(p); } • Same behavior as before ID1218, Christian Schulte
Freelist as Static Member #include <cstdlib> class IL { private: int _hd; IL* _tl; static IL* _free; public: IL(int h, IL* t = NULL); inthd() const; void hd(int h); IL* tl() const; void tl(IL* t); static void* operator new(size_t s); static void operator delete(void* p); static void flush(); }; ID1218, Christian Schulte
Implementation File: Broken IL* IL::_free = NULL; void IL::flush() { for (IL* f=_free; f!=NULL; f=f->_tl) free(f); _free = NULL; } • Freelist is initially empty • Access after free! ID1218, Christian Schulte
Implementation File: Better IL* IL::_free = NULL; void IL::flush() { IL* f=_free; while (f!=NULL) { IL* t = f->_tl; free(f); f = t; } _free = NULL; } ID1218, Christian Schulte
Allocating from the Freelist inline void* IL::operator new(size_t s) { if (_free == NULL) { return malloc(s); } else { IL* il = _free; _free = _free->_tl; return il; } } • Same behavior as before ID1218, Christian Schulte
Deallocating to the Freelist inline void IL::operator delete(void* p) { IL* il = static_cast<IL*>(p); il->_tl = _free; _free = il; } ID1218, Christian Schulte
Generic Freelists • Not straightforward • no static members in parametric classes • Solution • implement type independent freelist manager • pass freelist manager as extra argument to new and delete (placement new and delete) • read a book… ID1218, Christian Schulte
Reference Counting ID1218, Christian Schulte
Sharing Arrays • Desired functionality • resizable array • without copies (use a lot of memory) • same array • What memory management policy • unshared arrays: whoever created array is responsible (directly or indirectly) to delete • shared array: when is the array not any longer used? ID1218, Christian Schulte
Reference Counting • Separate • array proper: store elements • handle: implement allocation protocol provide access to array proper • Store in array proper: reference count …how many handles refer to array • array creation through handle: just one • when array becomes shared through additional handle: increment • when handle is deleted: decrement • if reference count hits zero: delete array ID1218, Christian Schulte
Array Object Proper class AO { public: unsigned int n; int* x; unsigned int rc; }; ID1218, Christian Schulte
Array Handle class AH { private: AO* _ao; public: AH(unsigned int n=8); AH(const AH& ah); AH& operator=(const AH& ah); ~AH(); int& operator[](unsigned inti); … }; ID1218, Christian Schulte
Array Handle Constructor AH::AH(unsigned int n) : _ao(new AO) { _ao->n = n; _ao->x = new int[n]; _ao->rc = 1; } ID1218, Christian Schulte
Array Handle: Object Access inline int& AH::operator[](unsigned int i) { return _ao->x[i]; } … ID1218, Christian Schulte
Array Handle Destructor inline AH::~AH() { _ao->rc--; if (_ao->rc == 0) { delete [] _ao->x; delete _ao; } } • Only if no more handles refer to object, delete object • Different design: provide constructor and destructor for array object ID1218, Christian Schulte
Copy Constructor inline AH::AH(const AH& ah) : _ao(ah._ao) { _ao->rc++; } • Array object is used by one additional handle ID1218, Christian Schulte
Assignment Operator inline AH::operator=(const AH& ah) { if (this != &ah) { if (--_ao->rc == 0) { delete _ao->x; delete _ao; } _ao = ah._ao; _ao.rc++; } return *this; } • Unsubscribe handle from current object • possibly delete object • Subscribe to new object ID1218, Christian Schulte
Putting the Object at its Place… class AH { private: class AO { public: unsigned int n; int* x; unsigned intrc; }; AO* _ao; public: AH(unsigned int n=8); … }; • Object should never be used without controlling handle ID1218, Christian Schulte
Smart Pointers • Generic wrapper class overloading operators -> and * • Part of C++ standard library • Use reference counting inside ID1218, Christian Schulte