Construct a DynObj from a variety of sources
You may want to create:
- An empty, i.e. null, DynObj; presumably, you will be giving it a DAO later;
- From a DAO: the DynObj takes ownership of it and represents it;
- From a std::auto_ptr: forces the auto pointer to give up the DAO, i.e. the DynObj takes ownership of the DAO in the auto_ptr and represents it; the auto_ptr is null after construction;
- From a DynTmp: so that you can transfer a DAO from one DynObj to another explicitly, i.e. without implicit copy.
The DAO or type of auto_ptr or DynTmp can be different from that of DynObj, the compiler will check that the conversion is legal. I.e.
class Foo {};
class Derived: public Foo {};
class Bad {};
DynObj<Derived> der;
RRef<Foo> foo(der);
RRef<Bad> foo2(der);
Construct an RRef from a variety of sources
You may want to create:
- An empty, i.e. null, RRef; presumably, you will be giving it an object to represent later;
- From a DynObj: the RRef represents it; the object type is allowed to be different, so long as it is allowed by the compiler;
- From another RRef, of same or different object type: since it is just a reference, shallow copy makes sense
- From an RRefable: so that an automatic variable (i.e. one created on the stack) can be represented by an RRef
Change the DAO represented by a DynObj
You may want a DynObj to represent a different DAO. This is done by one of the acquire() methods. Provide the overloaded versions of acquire() as for the DynObj constructor.
Change the object represented by an RRef
You may want a RRef to represent a different object. This is done by one of the reset(C) methods, which each have an operator=(C) counterpart and an RRef(C) constructor counterpart.
Reset a DynObj or RRef to a default state
You may want to reset a DynObj or RRef to represent nothing, as though it had just been default construted. This is done with the reset() method (takes no argument).
Make DynObj release a DAO from management
You may want a DynObj to stop representing a DAO without destroying it, i.e. you take control of the destruction of the DAO. This would likely happen if you had to give control of the lifetime of the DAO to a third-party library or legacy code.
Swap two DynObjs or two RRefs
DynObj can't be copied so the default std::swap does not work with DynObj. A method called swap() is provided instead. As to RRef, a swap method is not necessary since RRefs can be shallow copied, but is provided for consistency.
Give access to DAO inside DynObj and RRef
The DynObj and RRef proxies should give access to the DAO via a function, or member function or operator. Want something short and easy to type. Options were:
- function: e.g. getObj(dynObj): probably counter intuitive since DAO is in DynObj which suggests member
- method: e.g. dynObj.getObj(): too long
- operator->: too pointer-like
- operator.: can't be overloaded
- operator(): like "method with no name", and most neutral, so seems best bet. This operator returns reference rather than pointer, since otherwise requires rather verbose dynObj()->method() for chaining, not very convenient, plus also too pointer like.
Therefore, use operator() to get a reference to the object stored in DynObj or RRef, and chain with a member function or data if needed:
DynObj<Foo> foo( new Foo );
foo().method();
foo() = 2;
Assert before access DAO
Many times you "know" that a DynObj or RRef is not null, but in non-release build of your code this should be asserted "just in case". Yet it is easy to forget this assertion, so make it automatic in the call to operator() of both DynObj and RRef. I.e. if you forget that a DynObj or RRef is null before making a (illegal) call on its operator(), an assertion will fail, something like:
DynObj<Foo> dynObj;
dynObj().method();
assertion "NoPtr::isNotNull(_obj)" failed at line 300 of file DynObj.hh.
Allow you to get pointer to object in RRef or DynObj
You may need to give address of object stored in RRef or DynObj to legacy code, third party library, etc. This could be done by using & (dynObj())
assuming a DynObj called dynObj. However, you may not want the assertion, but just the address, so have an extra method that directly returns a pointer to the object stored in RRef or DynObj. RRef<Foo> foo(dynObj);
legacyFunc(foo.getPtr());
legacyFunc(& foo());
Test nullness of DynObj and RRef
Since operator() returns a reference, you need a way to test that an object is indeed contained. You could use getPtr() == NULL, but it is more expressive to use the isNull() or isNotNull() member functions or free functions: if (dynObj.isNotNull()) dynObj().method();
if (isNotNull(dynObj)) dynObj().method();
Provide usual comparison operators for objects
You may need to test if two DynObj are different, equal, or ordered, and similarly for RRef. This comparison uses the operators defined on the objects represented, rather than the pointers to the objects, since DynObj and RRef represent the objects they hold rather than pointers. Thus are provided operator==, operator!= and operator< for both DynObj and RRef, independent of the Context template parameter (see the STL uses for what the Context is).
Support polymorphism
Allow the DAO stored to be of a subclass of the class stored. E.g. DynObj<T> and RRef<T> can be given an object of type Derived_of_T: class Base {...};
class Derived: public Base {};
DynObj<Base> baseObj( new Derived );
RRef<Base> refObj(baseObj);
In this case, make sure, as with raw pointers, that your Base class has a virtual destructor. A special deleter that "remembers" the true type of the DAO stored is not supported since the language does not support it for normal polymorphic use. Supporting this could favor serious bugs if your class doesn't define a virtual destructor just because you know NoPtr doesn't require one, and you end up using your class outside of NoPtr, say in another application.
Hide your implementation
Support pimpl idiom. I.e., allow DynObj and RRef to be data members of a class without the full class definition of their template parameter. This is one use of DAO's and references that is very common. E.g.
class Bar;
class Foo {
void method();
DynObj<Bar> bar;
};
#include "Bar.hh"
void method() {
}
Minimal namespace pollution
"using namespace NoPtr;" brings only user interface into global namespace. Anything that you don't directly need is put in a separate namespace (currently NoPtr::NoPtrImpl).
Note never put a "using" declaration in a header file.
Constness of proxy vs of object contained
You can express the constness of the object stored by DynObj or RRef independently from the constness of the DynObj or RRef, so that they can be made to store a different object if required. Using DynObj as an example:
- DynObj<T> is a non-const proxy for a non-const T; i.e. the proxy can be changed to represent a different T instance, and the instance stored is non-const;
- DynObj<const T> is non-const proxy for a const T; i.e. the proxy can be changed to represent a different T instance, but the instance cannot have its data changed and can only have const methods called on it;
- const DynObj<T> is a const proxy for a non-const T; i.e., once constructed, the proxy can not be made to represent another T, the T it has is the one it will keep till it dies (can't even call release()). However, the T contained can have non-const methods called from it;
- const DynObj<const T> is a const proxy for a const T; i.e., the proxy can not be made to represent another T after construction, and the T contained cannot be mutated.
Receive const obj
You can give an Owned<T*> or RRef<T> to a function that expects a link to a const T, as long as the Owned/RRef is passed by const reference": void function1(const DynObj<const Base>&);
void function2( DynObj<const Base>&);
void function3(const RRef<const Base>&);
void function4( RRef<const Base>&);
DynObj<Base> base;
function1(base);
function2(base);
function3(base);
function4(base);
Converting to a non-const DynObj or RRef is disallowed to enforce const correctness (just think about a bit it and you'll see why).
Compare two proxies
Comparing two DynObj or RRef objects does the comparison on the objects represented. This makes sense since neither are smart pointers, for which the comparison typically compares the raw pointers. Support for nullness must be intuitive:
- if two objects are null, then they compare equal
- if only one object is null, they they compare unequal
- the objects represented are compared only if both are non-null
Transfer DAO
Transfer of ownership must be allowed. Provide an acquire() method when one DynObj takes possession of a DAO owned by another DynObj, and a giveAway() method when one DynObj wants to give away the the DAO it was representing. In the latter case, the DAO must be destroyed if no one is there to "take it over". Therefore, the giveAway() returns a DynTmp rather than a pointer to the DAO. DynObj<T> dynObj1(new T);
DynObj<T> dynObj2(dynObj1.giveAway());
DynObj<T> dynObj3;
dynObj3.acquire(dynObj2);
Next usage pages:
Generated on Mon Aug 4 18:51:33 2003 for NoPtr C++ Library by
1.3.2