80 likes | 91 Views
Review: for non-static built-in (native) types default constructor and destructor do nothing copy constructor and assignment operator (re-)value member-wise Review: variables vs. the pointers and references to them may have different scopes May result in obvious kinds of lifetime errors
E N D
Review: for non-static built-in (native) types default constructor and destructor do nothing copy constructor and assignment operator (re-)value member-wise Review: variables vs. the pointers and references to them may have different scopes May result in obvious kinds of lifetime errors But may also introduce more subtle issues of aliasing Can risk destroying an object too soon or too late Basic ideas in memory management with classes Constructor allocates, destructor de-allocates: an object “owns” memory Shallow copy vs. deep copy: aliasing vs. duplication Memory Management with Classes
Resources allocated by an object need to be freed Constructor, destructor offer good start/end points Example: class to hold a fixed sized array Can store integer values Can access each element Array size may differ for each object Once set, object’s array size is fixed #ifndef INTARRAY_H #define INTARRAY_H class IntArray { public: IntArray(size_t); ~IntArray(); int & operator[](size_t); IntArray(const IntArray &); private: size_t m_size; int *m_values; }; #endif /* INTARRAY_H */ Constructor Allocates, Destructor De-allocates
Initialization list sets m_sizeto the passed length, and also sets m_valuesto0 Only allocate if length is greater than zero What does new do? Allocates memory Calls constructor(s) Destructor releases the allocated array via delete Calls elements’ destructors Returns memory to the heap #include “IntArray.h" IntArray::IntArray(size_t s) :m_size(s), m_values(0) { if (m_size > 0) { m_values = new int[m_size]; } } IntArray::~IntArray() { delete [] m_values; } int & IntArray::operator[](size_t s){ if (!m_values || s >= m_size) { throw 1; } return m_values [s]; } Constructor/Destructor, continued
So, scopes are tied together IntArray and its memory What does delete do? Calls destructor(s) Frees memory What are new [] and delete [] vs. new and delete? What if m_values was 0 when delete was called on it? Remember what happens? int main(int, char*[]) { IntArray a(6); // When a is created, it // usually allocates some // memory (here, 6 ints) return 0; // Now a is destroyed, and // since it’s destructor // is called, so is the // memory it allocated } Constructor/Destructor, continued
There are two ways to “copy” Shallow: aliases existing resources Deep: makes a complete and separate copy Version above shows shallow copy Efficient But may be risky. Why? Hint: destructor (how to fix?) What’s the invariant established by this code? Hint: what can we say about m_size and m_values? // just uses the array that’s already in the other object IntArray::IntArray(const IntArray &a) :m_size(a.m_size), m_values(a.m_values) { } Copy Constructor: Shallow Copy
This code shows deep copy Safe: no shared aliasing But may not be very efficient Note trade-offs with arrays Allocate memory once More efficient than multiple calls to new (heap search) Constructor and assignment called on each array element Less efficient than block copy E.g., using memcpy() But sometimes more correct i.e., constructors and destructors are called // makes its own copy of the array IntArray::IntArray(const IntArray &a) :m_size(a.m_size), m_values(0) { if (m_size > 0) { m_values = new int[m_size]; for (size_t i = 0; i < m_size; ++i) { m_values[i] = a.m_values[i]; } } } Copy Constructor: Deep Copy
Also Think About When to Use “new” Objects have to live beyond current scope Is object copying a viable solution? Good for copy : Card objects Object size is small Non-polymorphic Bad for copy : Player, Game objects. Involve heap memory allocation Polymorphic Is object swapping a good alternative? Good for objects with embedded heap objects such as Player, std::string All STL containers, like std::vector, std::set, std::map, …, etc. Not useful for polymorphic classes Game* makeGame(const char* name) { Game* result = 0; if (strcmp(name,”bridge”)==0) result= new BridgeGame; else if (strcmp(name,”hearts”)==0) result= new HeartsGame; return result; }
Know what goes where in memory Understand the mechanics of stack vs. heap allocation Watch for simple lifetime errors Think about (and sketch) more subtle scope/lifetime issues Pay attention to aliasing issues Think about shallow copy vs. deep copy (problems/trade-offs) Summary: Memory Management With Classes