5.0 KiB
Usage of smart pointers in sfntly C++ port
In sfntly C++ port, an object ref-counting and smart pointer mechanism is implemented. The implementation works very much like COM.
Ref-countable object type inherits from RefCounted<>, which have addRef() and release() just like IUnknown in COM (but no QueryInterface). Ptr<> is a smart pointer class like CComPtr<> which is used to hold the ref-countable objects so that the object ref count is handled correctly.
Lets take a look at the example:
class Foo : public RefCounted<Foo> {
public:
static Foo* CreateInstance() {
Ptr<Foo> obj = new Foo(); // ref count = 1
return obj.detach(); // Giving away the control of this instance.
}
};
typedef Ptr<Foo> FooPtr; // Common short-hand notation.
FooPtr obj;
obj.attach(Foo::CreateInstance()); // ref count = 1
// Take over control but not bumping
// ref count.
{
FooPtr obj2 = obj; // ref count = 2
// Assignment bumps up ref count.
} // ref count = 1
// obj2 out of scope, decrements ref count
obj.release(); // ref count = 0, object destroyed
Notes on usage
- Virtual inherit from RefCount interface in base class if smart pointers are going to be defined.
- All RefCounted objects must be instantiated on the heap. Allocating the object on stack will cause crash.
- Be careful when you have complex inheritance. For example,
In this case the smart pointer is pretty dumb and dont count on it to nicely destroy your objects as designed. Try refactor your code like
class I; // the common interface and implementations class A : public I, public RefCounted<A>; // A specific implementation class B : public I, public RefCounted<B>; // B specific implementation
- Smart pointers here are very bad candidates for function parameters and return values. Use dumb pointers when passing over the stack.
- When down_cast is performed on a dangling pointer due to bugs in code, VC++ will generate SEH which is not handled well in VC++ debugger. One can use WinDBG to run it and get the faulting stack.
- Idioms for heap object as return value
If you are not passing that object back, you are the end of scope.
Be very careful when using the assignment operator as opposed to attaching. This can easily cause memory leaks. Have a look at The difference between assignment and attachment with ATL smart pointers. Since were using essentially the same model, it applies in pretty much the same way. The easiest way of knowing when to Attach and when to assign is to check the declaration of the function you are using.
// Attach to the pointer returned by GetInstance static CALLER_ATTACH FontFactory* GetInstance(); // Assign pointer returned by NewCMapBuilder CMap::Builder* NewCMapBuilder(const CMapId& cmap_id, ReadableFontData* data);
Detecting Memory Leak
The implementation of COM-like ref-counting and smart pointer remedies the lack of garbage collector in C++ to certain extent, however, it also introduces other problems. The most common one is memory leakage. How do we know that our code leak memory or not?
- For Linux/Mac, valgrind http://valgrind.org is very useful.
- For Windows, the easiest is to use Visual C++ built-in CRT debugging http://msdn.microsoft.com/en-us/library/x98tx3cf(v=vs.80).aspx
Useful Tips for Debugging Ref-Couting Issue
- Define
ENABLE_OBJECT_COUNTER
andREF_COUNT_DEBUGGING
. All ref-count related activity will be ouput to stderr.
- The logs will look like (under VC2010)
Use your favorite editor to transform them into SQL statements, e.g. Regex pattern
^([ACDR]) class RefCounted<class sfntly::([A-Za-z0-9:]+)>[ *const]+:oc=([-0-9]+),oid=([0-9]+),rc=([-0-9]+)
Replace to
insert into log values(\1, \2, \3, \4, \5);
- Add one line to the beginning of log
- Run sqlite shell, use .read to input the SQL file.
- Run following commands to get the leaking object class and object id:
- Once you know which object is leaking, its much easier to setup conditional breakpoints to identify the real culprit.