To minimize the number of classes you have to remember, NoPtr defines a "tweak" parameter that you specify when using a DynObj inside an STL container: thus was born the class DynObj<TT>::InValueContainer
. You must think of this as "DynObj<TT>, inside a value-based container", rather than as a separate class. Indeed, DynObj<TT> and DynObj<TT>::InValueContainer (as well as any of the other implementations DynObj<TT>::InValueContainer*) interact seamlessly.
E.g. the following traditional code
class Foo { private: typedef std::list<Bar*> list; list obj; };
#include "DynObj.hh" class Foo { private: typedef std::list<DynObj<Bar>::InValueContainer> list; list obj; };
class Foo { ... public: void f() {Bar* bar = list.front(); list.front() = NULL;} };
#include "DynObj.hh" class Foo { ... public: void f() {DynObj<Bar> bar( list.front().giveAway() );} };
Foo* foo1 = new Foo; list.push_back(foo1); assert(foo1 != NULL);// true list.push_back(new Foo);
DynObj<Foo> foo1(new Foo); list.push_back(foo1.giveAway()); assert(foo1.isNull());// true list.push_back(makeDynObj(new Foo));
To minimize the chance of this happening, the DynObj contains code that asserts that any access to, or deletion of, the DAO held, is done while only one copy of the container exists. Therefore, if you access any method or member of the DAO, or it gets destroyed, while more than one copy of the container exists, you will get a failed runtime assertion of the form
assertion failed: "soleOwner()", file DynObj.hh, line N
N is not important. If you see this, find which object caused this, which container it belongs to, and when that container was copied.
Thus was born the DynObj<TT>::InValueContainerOpt1 class (Opt1 = optimization level 1). It is an optimized version of DynObj<TT>::InValueContainer. Use it only once you know your program is working with DynObj<TT>::InValueContainer: you can then try replacing one declaration of it with DynObj<TT>::InValueContainerOpt1, redo a debug build, rerun and see if you get a failed assertion.
DynObj<TT>::InValueContainerOpt1 works for any depth of copy/assign within a container, but will fail if two independent copy/assign take place on the same DynObj, e.g. inside one of the std:: algorithms. At this point in time, I am unaware of an STL algorithm that would require this. In any case, a failed assertion (see below) after doing the change means you are limited to the fool-proof DynObj<TT>::InValueContainer.
Yet a further optimization is possible on the implementation and is called DynObj<TT>::InValueContainerOpt2. The assumption that InValueContainerOpt2 makes is that at most one-level of implicit copy/assignment will be used by the STL implementation when doing operations that use copy/assign, such as push_back, insert, etc, and std::sort etc.
The failed assertions, indicating that DynObj<TT>::InValueContainerOpt1 or 2 is insufficient and you should use the slower one, will be something like:
assertion failed: "isConnected()", file Contexts.hh, line N
or
assertion failed: "soleOwner()", file Contexts.hh, line N
Again N is unimportant.