Main Page Namespace List Class Hierarchy Alphabetical List Compound List File List Namespace Members Compound Members File Members Related Pages
This page discusses the use of Owned<P*> and other classes via some examples and issues to consider when using them.
The documentation page for each class also has examples of use. Here is a code example where a class A is an owner of a B* and class C is simply a user of the B* that is owned by the A. The B* will automatically die at the same time as the A, thereby invalidating the pointer held in C. But the C is able to check whether the B* still exists before calling its methods:
struct B {
void method() {}
void constMethod() const {}
};
class A {
Owned<B*> _ptr;
public:
A(): _ptr(new B) {}
};
class C {
BLink<const B> _ptr;
C(const BLink<const B>& p): _ptr(p) {}
void test() const {
assert(_ptr()->isNotNull());
_ptr()->method();
_ptr()->constMethod();
}
};
int main() {
A a;
C c(a._ptr);
c.test();
A a2 = a;
}
Here is an example of using an owned container. Class A has a list of pointers, which would normally require a destructor and a loop over all elements of the list to call delete on each. By using the Owned container, it doesn't have to worry about those memory management issues. Note how the container that is passed to the outside of A is the list itself rather than the owned list. This is recommend for this situation since the fact that the list is owned is an implementation detail to outsiders.
class A {
typedef std::list<int*> List;
Owned<List> list;
public:
A() { ... populate list...}
const List& getList() const {return list.cntr();}
};
Look at the files tests/testIdiom*.cc for various simple implementations of common patterns that illustrate some of the idioms important to C++. They are included here for your convenience:
- General:
- In source files (.cc, .C etc) you can add a "using namespace MangoPtr;" to bring the contents of namespace MangoPtr into the global namespace. Never do this in header files (for any library).
- Just instantiate a MangoPtr::Owned<T> (where T must be container of pointers), MangoPtr::Owned<T*>, MangoPtr::Owned<T*, OShared> or a MangoPtr::BLink<T>, as shown in the class docs for each class.
- The type T can be a base class of your class, e.g. Owned<T> can be given an object of type Derived_of_T*:
class Base {...};
class Derived1: public Base {};
class Derived2: public Base {};
Owned<Derived1*> owner( new Derived2 );
BLink<Base> user(owner);
- Use operator() to obtain the pointer in the BLink or Owned:
Owned<Derived1*> owner( new Derived2 );
owner()->callDerived1Method();
- For pointers, you can express the constness of the Owned or BLink separately from that of the resource:
- Owned<T*> is an object for which neither T nor the owner are const;
- Owned<const T*> is an object that holds a const T, but the owner can be changed (reset to owner another object);
- const Owned<T*> is an object for which T is non-const, but ownership can't be changed
- const Owned<const T*> is an object for which neither T nor ownership of T can be changed
- You can give an Owned<T*> or BLink<T> to a function that expects a link to a const T, as long as the Owned/BLink is passed by const reference":
void function1(const Owned<const Base*>&);
void function2( Owned<const Base*>&);
void function3(const BLink<const Base*>&);
void function4( BLink<const Base*>&);
Owned<Base*> mango;
function1(owner);
function2(owner);
function3(owner);
function4(owner);
Converting to a non-const Owned or BLink is not possible to preserve const correctness: if you were allowed to pass an Owned<T> to a function expecting a non-const Owned<const T>, the function could store a const T in the Owned, and after return, you would be able to access non-const methods of the const T because you really have an Owned<T> rather than an Owned<const T>, clearly inappropriate and a breach of contract. - Comparing a BLink<T> or Owned<T*> to a pointer, is limited by the same rules of pointer comparison as for dumb pointers. This is particularly important where multiple inheritance is involved, since in this case two pointers that point to the same object are not necessarily the same, if they refer to different branches of the inheritence. The only real way of avoiding this is to change Owned and BLink so that all pointer comparisons be changed to object comparisons. I.e., instead of having Owned<T>owns(T*ptr) use bool operator==(T *, T *), have it use bool operator==(T, T). More research is needed before deciding in favor of this, since it may require that operators be defined for objects that would typically not require them.
- Owned<T>, Owned<T*> and BLink<T> provide a cloneObjNew() method that can be used to obtain a clone of the container of pointer held, using a call to operator new; i.e., it is a convenience method that, for pointers, calls new T(*owner()), and for containers, does a deep copy of each element via the operator new.
- Use the isNull() function to test for null pointers, instead of comparing to NULL; it is clearer, greppable, and doesn't rely on a macro like NULL often does. E.g., This function works if p is a pointer, an owned pointer or a BLink.
- All Owned types:
- If you have a container of pointers, or a container of containers of pointers, etc, use Owned<T>. If you have pointer, use Owned<T*> only in the function or class that owns the pointer, otherwise pass the pointer itself (T*) or a BLink<T>. If you absolutely need your pointer to have its ownership shared by several owners, use Owned<T*, OShared>.
- Transfer ownership between two owners by using the takeOwnership() and giveOwnership() methods. Remember that Ownership is a transaction object so it must not be stored. It can only be used as a temporary.
- If you want to use the owned pointers or owned containers of pointers polymorphically, make sure, as with raw pointers, that your Base class has a virtual destructor if it is used polymorphically by Owned, i.e. if you have Owned<Base*> or Owned<Cntr<Base*> > but store Derived* in them.
- Cloning of Owned only makes sense for shared ownership, in which case it is a shallow copy.
- Cloning for strict ownership is forbidden to make explicit when you have to copy; you then use the usual techniques as with normal pointers, i.e. call new T(*owner()); another reason for not providing automatic cloning is that the cloning method typically falls in one of three categories: using new, calling a virtual clone method of arbitrary name, or other. Since there is no way for the compiler to know which should be used, template specializations would have to be used, but they have the major inconvenience that the compiler must have seen them before they are used otherwise the default, new-based version would be called, or it must be specified every time by including a special file; there doesn't seem to be one perfect solution to this problem, but the above one will decrease the likelyhood of using new() when it was not intended, and is easier to use than having to include a template specialization everywhere copy is required.
- Do not give ownership of a local object to an external object, this is like passing a reference to a local object as return value; e.g.
Ownership<T> getObj() {
Owned<T> owner;
return owner.giveOwnership();
}
- Remember that with shared ownership, the pointer is deleted only when the last owner is destroyed; this can lead to non-deterministic destruction, which can lead to degraded performance or running out of certain system resources
- Transfer of ownership for owned containers is different than for pointers: the contents of one container are simply moved to the new owner, and the original owner is left with an empty container.
STL containers use value-based semantics. This will usually lead to expensive copy and assignment operations during operations on the containers. Often, such operations are undesirable because they are costly for the type of object stored, either in time, memory, or some other resource. Using containers of pointers is often a way of getting around this since pointers are simple, sortable values. However, using a strict Owned pointer in an STL container means the pointers are again considered as values, which defeats the purpose of using pointers in the first place.
This is an example of how the current STL containers are not very well suited for objects that should not be copied inside the container. Another consequence of this is that many algorithms on the STL containers currently do not work optimally when the element type is an Owned pointer. or BLink. This is because many algorithms use std::swap and std::iter_swap, but problems with namespace resolution mean that even if you overload std::swap, it is likely not to be found. Overloading iter_swap is complicated and error-prone. Therefore, you should use Owned<Container> instead of pure STL containers.
There are tricks you can use if you really want to store strict Owneds in STL containers, if you won't be doing any re-ordering of the container elements. You can create an empty Owned at a known place of the container (say the back), and only after that assign your dynamic object to it:
std::list<MangoPtr::Owned<T> > owners;
owners.push_back( MangoPtr::Owned<T>() );
owners.back().takeOwnership( new T );
You could create several empty owners at once, then have each take ownership of a newly allocated object. This however does not work with containers that require unique elements, or sort their elements, since all empty owners are the same from STL's perspective.
Generated on Tue Nov 12 20:44:01 2002 for Mango-ptr Library by
1.2.18