You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
146 lines
5.0 KiB
146 lines
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:
|
|
|
|
``` {.prettyprint}
|
|
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
|
|
>
|
|
> ``` {.prettyprint}
|
|
> 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](http://blogs.msdn.com/b/oldnewthing/archive/2009/11/20/9925918.aspx).
|
|
> 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.
|
|
>
|
|
> ``` {.prettyprint}
|
|
> // 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](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](http://msdn.microsoft.com/en-us/library/x98tx3cf(v=vs.80).aspx)
|
|
|
|
Useful Tips for Debugging Ref-Couting Issue
|
|
------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
- Define `ENABLE_OBJECT_COUNTER` and `REF_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
|
|
>
|
|
> ``` {.prettyprint}
|
|
> ^([ACDR]) class RefCounted<class sfntly::([A-Za-z0-9:]+)>[ *const]+:oc=([-0-9]+),oid=([0-9]+),rc=([-0-9]+)
|
|
> ```
|
|
|
|
> Replace to
|
|
>
|
|
> ``` {.prettyprint}
|
|
> 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.
|