There are several classes from other libraries that have related use. Some that I am currently aware of are:
DynObj does not support implicit move-on-copy semantics since: 1) this is too error prone; and, 2) this prevents it from being used in STL containers. However, when a DynObj goes out of scope it calls delete on the dynamically allocated object it represents. Currently, DynObj does not provide operator-> because it is a smart reference rather than a smart pointer. To be a smart pointer, a class must attempt to pass for a raw pointer in as many situations as possible. DynObj makes no such attempt since the idea of NoPtr is to move away from pointer semantics.
DynObj does several things that std::auto_ptr doesn't. For instance, you can pass a DynObj<T> to a function expecting a const reference to DynObj<const T> (it should be fairly obvious why a non-const reference is not allowed). DynObj supports explicit move semantics (moveToTmp() and giveAway() methods) which has none of the limitations of implicit move. Also, a DynObj<T> can be a data member of a class U even if the complete definition of T is not visible in the header for U. Last but not least, DynObj can be in STL containers while providing strict ownership semantics.
One feature difference: shared_ptr allows you to override how the destruction of the DAO will take place (including no destruction), which adds the ability to smarten pointers not just for DAO's but for COM objects (which are not necessarily DAO's: they may already exist on another machine), or handles etc. DynObj currently does not because there hasn't been a need and there is a non-negligible cost to that feature.
A design difference is that boost::weak_ptr is needed to prevent circular references when using shared_ptr, whereas no such class is needed for NoPtr::DynObj. A class, RRef, is provided, but based on a extra role that had to be supported, rather than a work-around against circular dependencies. Strict ownership requires you to follow the scope of the owner and greatly decreases the likelyhood of ownership inversion. For these reasons, strict ownership in the form of NoPtr provides somewhat cleaner separation of use from ownership and therefore clearer algorithms (though I must admit clarity of algorithm is much more a function of programmer than library :).
Though the mechanism used is different, both shared_ptr and DynObj allow you to pass a shared_ptr of T to a function expecting a const reference to one of const T. The different mechanism has the added advantage (for shared_ptr) that you can also pass to a function expecting non-const reference, and ref to base, but this can lead to unexpected results to the unwary user, and the disadvantage that it is more costly (due to reference counting).
Finally, NoPtr tries to use different classes for different roles: DynObj for DAO's, DynTmp for unnamed temporaries, RRef instead of references/pointers. It should be fairly rare to have to specify a DynObj in an interface, if that interface does not directly affect ownership. Most often, RRefs or raw pointers would be used instead. When shared ownership is an intrinsic part of your design, this leads to clearer semantics.
An important similarity is that, like boost::shared_ptr, NoPtr::DynObj works inside containers. Therefore, you should not have to use a shared_ptr if shared ownership isn't fundamentally part of your design.
For that reason, one could say that boost::scoped_ptr is a subset of NoPtr::DynObj. However, DynObj
Owned containers were a problem because of iterators: they make it impossible to centralize operations, and so things like std::list<Foo>::remove could not be made to call the necessary destructor, without replicating the complete interface for std::list, and similarly for other STL containers, clearly not a practical solution. Also, the SOSP was only a hack for STL containers, since I really didn't want it, prefering strict ownership, plus a very good and reliable boost::shared_ptr existed if shared ownership is really what I wanted.
Finally, Mango-ptr used reference counting for the debug capabilities, a costly implementation. I decided to redo the library from scratch, going back to the drawing board with regards to the concepts I wanted to provide.
The main differences are therefore:
However, I have a few reservations about Loki's SmartPtr:
Plus, it wouldn't be as much fun to just use someone else's library :)
Class | Ownership | Copying | Incomplete Types | Const-qualified |
Owned<T*> | sole | none | yes | yes |
RRef<T> | none | value | yes | yes |
The "const qualified" is much better in NoPtr: it is the same as for raw pointers to DAO's, something that the arglib pointers do not do. In fact, the arglib pointers all force constness of the DAO to be the same as that of the smart pointer, which is rarely a desirable feature. Instead, NoPtr allows you to specify if the proxy is const, or if the DAO is const, or both or neither.