OOP Class Notes for 11/19/13
Smart Pointers
- Smart Pointers
- With smart pointers we can mostly do what the Java garbage collector does, reclaim unused objects automatically.
- To do so, smart pointers need to keep track of the number of references.
- As an added benefit, we can get rid of annoying upcasts in our traslator scheme.
- Reference Count
- We want to track of the number of references that point to objects. How to achieve this? Where to put the counter?
- Inside the data layout. Don't implement this because code has to check for null values, as they have no data layout.
- Inside our
Ptr
. Does not work. When twoPtrs
point to the same object and one of them goes out of the scope, both counters in twoPtrs
need to be decremented. But it is impossible to access the otherPtr
. - In a seperate object and tag along with the actual object. %
- Update the counter when an object is declared, copied, destructed, and assigned. When the counter becomes zero, delete the object and its counter.
- One disadvantage of reference counting: cannot locate circularly referenced objects that are not referenced from outside.
- Implementation
- Constructors
/* size_t is a type used for storing unsigned integer independent from architectures. size_t comes from the <cstring> library. The default addr = 0 is for initializing array members. */ Ptr(T* addr = 0) : addr(addr), counter(new size_t(1)) { TRACE(addr); }
/* copy constructor for Ptr<U> (also covers the case for Ptr<T>) */ template<typename U> Ptr(const Ptr<U>& other) : addr((T*)other.addr), counter(other.counter) { TRACE(addr); ++(*counter); }
- Destructor
~Ptr() { TRACE() if (0 == --(*counter)) { if (0 != addr) addr->__vptr->__delete(addr); delete counter; } }
- We cannot simply delete
addr
becausePtr<T>
may not have the right type, i.e., represent a static Java superclass, and we thus do not know the correct size of the data layout. - We want to use a virtual destructor but we are not allowed to use
virtual methods in the translator. We can simulate virtual methods
by implementing a method
__delete()
in every object. - We cannot call
__delete(Ptr<T>)
, which calls the copy constructor that increments the counter. This results in repeated calls for__delete()
for the same object. So we call__delete(addr)
- Assignment operator
Ptr& operator=(const Ptr& right) { TRACE(addr); if (addr != right.addr) { if (0 == --(*counter)) { if (0 != addr) addr->__vptr->__delete(addr) delete counter; } addr = right.addr; counter = right.counter; ++(*counter); } return *this; }
- If the constructor, destructor, and assignment operator are not explicitly defined, the C++ compiler provides a default implementation. Other operators such as != and == need to be explicitly defined if they are needed.
- Constructors
- We want to track of the number of references that point to objects. How to achieve this? Where to put the counter?
- We need a special destructor for arrays
- Because we separately allocated
__data
on the heap, only after we delete__data
can we delete the data layout of the array.
friend
- We need to access the private fields of smart pointers of
different types (e.g.,
addr
andcounter
). Even though all smart pointers are generated by the same template, they represent different classes. These different classes cannot access their mutual private fields without violating C++ type safety. - We can access private data across different template
instances of
Ptr
by declaring all template instances friends of each other:template<typename U> friend class Ptr;