Memory Management: Reference Counting, Shared Pointers, Buffer Management, and More
Memory Management: Reference Counting, Shared Pointers, Buffer Management, and More
> SharedPtr
> Buffers
> Singletons
Reference Counting
Wikipedia
Reference Counting (cont'd)
> Whenever a reference is destroyed or overwritten, the reference
count of the object it references is decremented.
> When the reference count of an object reaches zero, the object is
deleted.
> If the owner of an object fails to delete it, this will result in a
memory leak.
> Others may point to the object, too, but they will never delete
the object.
> Ownership is transferable. But at any given time there can only
be one owner.
Reference Counting and Ownership
> A pointer taking ownership of an reference counted object does
not increment its reference count.
> This implies that there is no previous owner (in other words,
the object has just been created),
> or the previous owner gives up ownership (and thus does not
decrement the object's reference count).
> Usually, the first pointer an object is assigned to after its creation
takes ownership. All others do not.
The AutoPtr Class Template
> Poco::AutoPtr implements a reference counting "smart" pointer.
> Poco::AutoPtr can be instantiated with any class that supports
reference counting.
> A class supporting reference counting must
> maintain a reference count (initialized to 1 at creation)
> implement a method void duplicate() that increments the
reference count
> implement a method void release() that decrements the
reference count and, when it reaches zero, deletes the object
AutoPtr Construction and Assignment
> When constructing an AutoPtr<C> from a C*, the AutoPtr takes
ownership of C (and its reference count remains unchanged).
using Poco::AutoPtr;
class RCO
{
public:
RCO(): _rc(1)
{
}
void duplicate()
{
++_rc; // Warning: not thread safe!
}
void release()
{
if (--_rc == 0) delete this; // Warning: not thread safe!
}
private:
int _rc;
};
int main(int argc, char** argv)
{
RCO* pNew = new RCO; // _rc == 1
AutoPtr<RCO> p1(pNew); // _rc == 1
AutoPtr<RCO> p2(p1); // _rc == 2
AutoPtr<RCO> p3(pNew, true); // _rc == 3
p2 = 0; // _rc == 2
p3 = 0; // _rc == 1
RCO* pRCO = p1; // _rc == 1
p1 = 0; // _rc == 0 -> deleted
// pRCO and pNew now invalid!
return 0;
}
// _rc == 0 -> deleted
RefCountedObject
> Poco::RefCountedObject implements thread-safe reference
counting semantics. As of 1.3.4 it uses platform-specific atomic
operations, if available (Windows, Mac OS X).
using Poco::RefCountedObject;
using Poco::AutoPtr;
protected:
~RCO()
{
}
};
int main(int argc, char** argv)
{
AutoPtr<RCO> pRCO(new RCO);
return 0;
}
AutoPtr Operators and Semantics
> Poco::AutoPtr support relational operators:
==, !=, <, <=, >, >=
return 0;
}
AutoPtr Caveats and Pitfalls
!
> Be extremely careful when assigning an AutoPtr to a plain
pointer, and then assigning the plain pointer to another
AutoPtr!
> Both AutoPtr's will claim ownership of the object. This is bad!
> Explicitly tell AutoPtr that it has to share ownership of the object:
A* pA = p1;
Poco::AutoPtr<A> p3;
// p3 = pA; // BAD! p3 assumes sole ownership
p3.assign(pA, true); // Okay: p3 shares ownership with p1
return 0;
}
AutoReleasePool
> The Poco::AutoReleasePool class template takes care of
(reference counted) objects that nobody else wants.
using Poco::AutoReleasePool;
class C
{
public:
C()
{
}
void release()
{
delete this;
}
};
int main(int argc, char** argv)
{
AutoReleasePool<C> pool;
C* pC = new C;
pool.add(pC);
pC = new C;
pool.add(pC);
return 0;
}
// all C's deleted
The SharedPtr Class Template
> Poco::SharedPtr implements reference counting for classes that
do not implement a reference count themselves.
!
(dereferencing, relational operators, etc.).
> Once you use SharedPtr for an object, never work with plain
pointers to that object again.
Reference
SharedPtr1
Counter1
SharedPtr3 Object1
Reference
Counter2
SharedPtr2 Object2
#include "Poco/SharedPtr.h"
#include <string>
#include <iostream>
using Poco::SharedPtr;
Poco::SharedPtr<std::string> p1(pString); // rc == 1
Poco::SharedPtr<std::string> p2(p1); // rc == 2
p2 = 0; // rc == 1
// p2 = pString; // BAD BAD BAD: multiple owners -> multiple delete
p2 = p1; // rc == 2
return 0;
}
// rc == 0 -> deleted
SharedPtr Operators and Semantics
> Poco::SharedPtr support relational operators:
==, !=, <, <=, >, >=
class A
{
public:
virtual ~A()
{
}
};
class B: public A
{
};
class C: public A
{
};
int main(int argc, char** argv)
{
Poco::SharedPtr<A> pA;
Poco::SharedPtr<B> pB(new B);
return 0;
}
SharedPtr and Arrays
> The default implementation of SharedPtr will simply call delete
pObj
> For this to work, classes and their instantiators (factory classes)
must be registered with the DynamicFactory.
The Instantiator Class Template
> An instantiator for a class C must always be a subclass of
Poco::AbstractInstantiator<Base>, where Base is a base class of C.
using Poco::DynamicFactory;
using Poco::SharedPtr;
class Base
{
};
SharedPtr<Base> pA = factory.createInstance("A");
SharedPtr<Base> pB = factory.createInstance("B");
return 0;
}
DynamicFactory and Instantiators
> For the Poco::Instantiator class template to work, the class to be
created must be default constructible.
using Poco::DynamicFactory;
using Poco::AbstractInstantiator;
class Base
{
};
private:
int _i;
};
class CInstantiator: public AbstractInstantiator<Base>
{
public:
CInstantiator(int i): _i(i)
{
}
private:
int _i;
};
int main(int argc, char** argv)
{
DynamicFactory<Base> factory;
factory.registerClass<A>("A");
factory.registerClass("C", new CInstantiator(42));
return 0;
}
Buffer Management
> When interfacing with legacy C libraries or operating system
calls, one often needs to supply a buffer of a certain size.
> If the buffer is larger than a few bytes, it must be allocated on the
heap.
> The end() method returns a pointer to the end of the buffer.
using Poco::Buffer;
std::cin.read(buffer.begin(), buffer.size());
std::streamsize n = std::cin.gcount();
std::string s(buffer.begin(), n);
return 0;
}
Memory Pools
> Many applications allocate and release buffers of a given size
very frequently.
> Allocating buffers on the heap has performance impacts, and can
lead to heap fragmentation.
using Poco::MemoryPool;
std::cin.read(buffer, pool.blockSize());
std::streamsize n = std::cin.gcount();
std::string s(buffer, n);
pool.release(buffer);
return 0;
}
Singletons
Wikipedia
Singletons
> If you absolutely must have a singleton...
class MySingleton
{
public:
MySingleton()
{
// ...
}
~MySingleton()
{
// ...
}
// ...
www.appinf.com | [email protected]
T +43 4253 32596 | F +43 4253 32096