#ifndef SMART_PTR_HPP #define SMART_PTR_HPP /*------------------------------------------------------------------------------ Author: Andy Rushton and Daniel Milton Copyright: (c) Andy Rushton, 2004; Daniel Milton 2005 License: BSD License, see ../docs/license.html Dan Milton: three simple pointer containers with single level access: - simple_ptr for simple types and classes - simple_ptr_clone for polymorphic class hierarchies - simple_ptr_nocopy for any class that cannot or should no be copied Andy Rushton: three smart pointer containers with two-layer access: - smart_ptr for simple types and classes - smart_ptr_clone for polymorphic class hierarchies - smart_ptr_nocopy for any class that cannot or should no be copied ------------------------------------------------------------------------------*/ #include "os_fixes.hpp" #include "exceptions.hpp" #include "persistent.hpp" #include "textio.hpp" #include #include //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // Simple pointer class //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// template class simple_ptr { public: ////////////////////////////////////////////////////////////////////////////// // member type definitions typedef T value_type; typedef T& reference; typedef const T& const_reference; ////////////////////////////////////////////////////////////////////////////// // constructors, assignments and destructors // create a null pointer simple_ptr(void); // create a pointer containing a *copy* of the object // this copy is taken because the pointer class maintains a dynamically allocated object // and the T& may not be (usually is not) dynamically allocated // constructor form simple_ptr(const T& data); // assignment form for an already-constructed smart-pointer simple_ptr& operator=(const T& data); // copy constructor implements counted referencing - no copy is made simple_ptr(const simple_ptr& r); // assignment of smart pointers implement counted referencing - no copy is made simple_ptr& operator=(const simple_ptr&); // create a pointer containing a dynamically created object // Note: the object must be allocated *by the user* with new // constructor form - must be called in the form simple_ptr x(new type(args)) explicit simple_ptr(T* data); // assignment form simple_ptr& operator= (T* data); // destructor decrements the reference count and delete only when the last reference is destroyed ~simple_ptr(void); ////////////////////////////////////////////////////////////////////////////// // logical tests to see if there is anything contained in the pointer since it can be null // there are two forms:explicit and implicit // implicit: if(!r) or if(r) // explicit: if(r.null()) or if(r.present()) operator bool(void) const; bool operator!(void) const; bool present(void) const; bool null(void) const; ////////////////////////////////////////////////////////////////////////////// // dereference operators and functions // dereference the smart pointer to get the object - use in the form *p1 T& operator*(void) throw(null_dereference); const T& operator*(void) const throw(null_dereference); // used as a prefix to a member access to the contained object e.g. p1->print() calls T::print() T* operator->(void) throw(null_dereference); const T* operator->(void) const throw(null_dereference); ////////////////////////////////////////////////////////////////////////////// // explicit function forms of the above assignment and dereference operators // set the value void set_value(const T& data); // get the value T& value(void) throw(null_dereference); const T& value(void) const throw(null_dereference); // set the pointer // deletes the previous pointer and adopts the passed pointer instead // Note: the object must be allocated *by the user* with new // Warning: it is very easy to break the memory management with this operation void set(T* data = 0); // get the pointer T* pointer(void); const T* pointer(void) const; ////////////////////////////////////////////////////////////////////////////// // functions to manage counted referencing // test whether two pointers point to the same object(known as aliasing the object) // used in the form if(a.aliases(b)) bool aliases(const simple_ptr&) const; // find the number of aliases - used when you need to know whether an object is still referred to from elsewhere (rare!) unsigned alias_count(void) const; // make this pointer unique with respect to any other references to the same object // if this pointer is already unique, it does nothing - otherwise it copies the object void make_unique(void); // delete the object and make the pointer null - does not make it unique first, so all other pointers to this will be null too void clear(void); // make the pointer unique and null in one step - does not affect other pointers that were pointing to the same object void clear_unique(void); // make this pointer a unique copy of the parameter // useful for expressions like p1.copy(p2) which makes p1 a pointer to a unique copy of the contents of p2 void copy(const simple_ptr&); // alternate form used in assignments: p1 = ps.copy() simple_ptr copy(void) const; // persistence functions void dump(dump_context& str) const throw(persistent_dump_failed); void restore(restore_context& str) throw(persistent_restore_failed); protected: T* m_pointer; unsigned* m_count; }; //////////////////////////////////////////////////////////////////////////////// // comparisons required for using this class in an STL container // These require == and < operator in the contained type T // the remaining relational operators are provided by template functions // a null pointer is less-than a non-null, two nulls are equal // these funcions are defined as non-members so that you only need provide // the underlying T::operator< and == if you are going to use these functions template bool operator==(const simple_ptr&, const simple_ptr&); template bool operator<(const simple_ptr&, const simple_ptr&); //////////////////////////////////////////////////////////////////////////////// // string/print utilities template std::string simple_ptr_to_string(const simple_ptr& ptr, std::string null_string); template otext& print_simple_ptr(otext& str, const simple_ptr& ptr, std::string null_string); template otext& print_simple_ptr(otext& str, const simple_ptr& ptr, unsigned indent, std::string null_string); //////////////////////////////////////////////////////////////////////////////// // persistence - call these rather than the methods // the dump routine dumps simple_ptr-specific information and then calls dump_pointer on the contents // similarly the restore routine calls restore_pointer // so therefore the class T should have non-member dump/restore functions template void dump_simple_ptr(dump_context& str, const simple_ptr& data) throw(persistent_dump_failed); template void restore_simple_ptr(restore_context& str, simple_ptr& data) throw(persistent_restore_failed); //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // Cloning simple pointer class for polymorphic class hierarchies // The contained class T should implement the clonable interface defined in clonable.hpp //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// template class simple_ptr_clone { public: ////////////////////////////////////////////////////////////////////////////// // member type definitions typedef T value_type; typedef T& reference; typedef const T& const_reference; ////////////////////////////////////////////////////////////////////////////// // constructors, assignments and destructors // create a null pointer simple_ptr_clone(void); // create a pointer containing a *copy* of the object // this copy is taken because the pointer class maintains a dynamically allocated object // and the T& may not be (usually is not) dynamically allocated // constructor form simple_ptr_clone(const T& data); // assignment form for an already-constructed smart-pointer simple_ptr_clone& operator=(const T& data); // copy constructor implements counted referencing - no copy is made simple_ptr_clone(const simple_ptr_clone& r); // assignment of smart pointers implement counted referencing - no copy is made simple_ptr_clone& operator=(const simple_ptr_clone&); // create a pointer containing a dynamically created object // Note: the object must be allocated *by the user* with new // constructor form - must be called in the form simple_ptr_clone x(new type(args)) explicit simple_ptr_clone(T* data); // assignment form simple_ptr_clone& operator= (T* data); // destructor decrements the reference count and delete only when the last reference is destroyed ~simple_ptr_clone(void); ////////////////////////////////////////////////////////////////////////////// // logical tests to see if there is anything contained in the pointer since it can be null // there are two forms:explicit and implicit // implicit: if(!r) or if(r) // explicit: if(r.null()) or if(r.present()) operator bool(void) const; bool operator!(void) const; bool present(void) const; bool null(void) const; ////////////////////////////////////////////////////////////////////////////// // dereference operators and functions // dereference the smart pointer to get the object - use in the form *p1 T& operator*(void) throw(null_dereference); const T& operator*(void) const throw(null_dereference); // used as a prefix to a member access to the contained object e.g. p1->print() calls T::print() T* operator->(void) throw(null_dereference); const T* operator->(void) const throw(null_dereference); ////////////////////////////////////////////////////////////////////////////// // explicit function forms of the above assignment and dereference operators // set the value void set_value(const T& data); // get the value T& value(void) throw(null_dereference); const T& value(void) const throw(null_dereference); // set the pointer // deletes the previous pointer and adopts the passed pointer instead // Note: the object must be allocated *by the user* with new // Warning: it is very easy to break the memory management with this operation void set(T* data = 0); // get the pointer T* pointer(void); const T* pointer(void) const; ////////////////////////////////////////////////////////////////////////////// // functions to manage counted referencing // test whether two pointers point to the same object(known as aliasing the object) // used in the form if(a.aliases(b)) bool aliases(const simple_ptr_clone&) const; // find the number of aliases - used when you need to know whether an object is still referred to from elsewhere (rare!) unsigned alias_count(void) const; // make this pointer unique with respect to any other references to the same object // if this pointer is already unique, it does nothing - otherwise it copies the object void make_unique(void); // delete the object and make the pointer null - does not make it unique first, so all other pointers to this will be null too void clear(void); // make the pointer unique and null in one step - does not affect other pointers that were pointing to the same object void clear_unique(void); // make this pointer a unique copy of the parameter // useful for expressions like p1.copy(p2) which makes p1 a pointer to a unique copy of the contents of p2 void copy(const simple_ptr_clone&); // alternate form used in assignments: p1 = ps.copy() simple_ptr_clone copy(void) const; // persistence functions void dump(dump_context& str) const throw(persistent_dump_failed); void restore(restore_context& str) throw(persistent_restore_failed); protected: T* m_pointer; unsigned* m_count; }; //////////////////////////////////////////////////////////////////////////////// // comparisons required for using this class in an STL container // These require == and < operator in the contained type T // the remaining relational operators are provided by template functions // a null pointer is less-than a non-null, two nulls are equal // these funcions are defined as non-members so that you only need provide // the underlying T::operator< and == if you are going to use these functions template bool operator==(const simple_ptr_clone&, const simple_ptr_clone&); template bool operator<(const simple_ptr_clone&, const simple_ptr_clone&); //////////////////////////////////////////////////////////////////////////////// // string/print utilities template std::string simple_ptr_clone_to_string(const simple_ptr_clone& ptr, std::string null_string); template otext& print_simple_ptr_clone(otext& str, const simple_ptr_clone& ptr, std::string null_string); template otext& print_simple_ptr_clone(otext& str, const simple_ptr_clone& ptr, unsigned indent, std::string null_string); //////////////////////////////////////////////////////////////////////////////// // persistence - call these rather than the methods // the dump routine dumps simple_ptr_clone-specific information and then calls dump_interface // similarly the restore routine calls restore_interface // so therefore the class T should implement the persistent interface defined by the class persistent in persistent.hpp template void dump_simple_ptr_clone(dump_context& str, const simple_ptr_clone& data) throw(persistent_dump_failed); template void restore_simple_ptr_clone(restore_context& str, simple_ptr_clone& data) throw(persistent_restore_failed); //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // No-copy simple pointer class for managing objects that cannot be copied //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// template class simple_ptr_nocopy { public: ////////////////////////////////////////////////////////////////////////////// // member type definitions typedef T value_type; typedef T& reference; typedef const T& const_reference; ////////////////////////////////////////////////////////////////////////////// // constructors, assignments and destructors // create a null pointer simple_ptr_nocopy(void); // copy constructor implements counted referencing - no copy is made simple_ptr_nocopy(const simple_ptr_nocopy& r); // assignment of smart pointers implement counted referencing - no copy is made simple_ptr_nocopy& operator=(const simple_ptr_nocopy&); // create a pointer containing a dynamically created object // Note: the object must be allocated *by the user* with new // constructor form - must be called in the form simple_ptr_nocopy x(new type(args)) explicit simple_ptr_nocopy(T* data); // assignment form simple_ptr_nocopy& operator= (T* data); // destructor decrements the reference count and delete only when the last reference is destroyed ~simple_ptr_nocopy(void); ////////////////////////////////////////////////////////////////////////////// // logical tests to see if there is anything contained in the pointer since it can be null // there are two forms:explicit and implicit // implicit: if(!r) or if(r) // explicit: if(r.null()) or if(r.present()) operator bool(void) const; bool operator!(void) const; bool present(void) const; bool null(void) const; ////////////////////////////////////////////////////////////////////////////// // dereference operators and functions // dereference the smart pointer to get the object - use in the form *p1 T& operator*(void) throw(null_dereference); const T& operator*(void) const throw(null_dereference); // used as a prefix to a member access to the contained object e.g. p1->print() calls T::print() T* operator->(void) throw(null_dereference); const T* operator->(void) const throw(null_dereference); ////////////////////////////////////////////////////////////////////////////// // explicit function forms of the above assignment and dereference operators // get the value T& value(void) throw(null_dereference); const T& value(void) const throw(null_dereference); // set the pointer // deletes the previous pointer and adopts the passed pointer instead // Note: the object must be allocated *by the user* with new // Warning: it is very easy to break the memory management with this operation void set(T* data = 0); // get the pointer T* pointer(void); const T* pointer(void) const; ////////////////////////////////////////////////////////////////////////////// // functions to manage counted referencing // test whether two pointers point to the same object(known as aliasing the object) // used in the form if(a.aliases(b)) bool aliases(const simple_ptr_nocopy&) const; // find the number of aliases - used when you need to know whether an object is still referred to from elsewhere (rare!) unsigned alias_count(void) const; // delete the object and make the pointer null - does not make it unique first, so all other pointers to this will be null too void clear(void); // make the pointer unique and null in one step - does not affect other pointers that were pointing to the same object //void clear_unique(void); FIXME protected: T* m_pointer; unsigned* m_count; }; //////////////////////////////////////////////////////////////////////////////// // comparisons required for using this class in an STL container // These require == and < operator in the contained type T // the remaining relational operators are provided by template functions // a null pointer is less-than a non-null, two nulls are equal // these funcions are defined as non-members so that you only need provide // the underlying T::operator< and == if you are going to use these functions template bool operator==(const simple_ptr_nocopy&, const simple_ptr_nocopy&); template bool operator<(const simple_ptr_nocopy&, const simple_ptr_nocopy&); //////////////////////////////////////////////////////////////////////////////// // string/print utilities template std::string simple_ptr_nocopy_to_string(const simple_ptr_nocopy& ptr, std::string null_string); template otext& print_simple_ptr_nocopy(otext& str, const simple_ptr_nocopy& ptr, std::string null_string); template otext& print_simple_ptr_nocopy(otext& str, const simple_ptr_nocopy& ptr, unsigned indent, std::string null_string); //////////////////////////////////////////////////////////////////////////////// // there's no persistence on simple_ptr_nocopy because the whole point is that // it stores an uncopyable object and persistence is a form of copying //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // Simple smart pointer class //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // internals template class smart_ptr_holder; //////////////////////////////////////////////////////////////////////////////// template class smart_ptr { public: ////////////////////////////////////////////////////////////////////////////// // member type definitions typedef T value_type; typedef T& reference; typedef const T& const_reference; ////////////////////////////////////////////////////////////////////////////// // constructors, assignments and destructors // create a null pointer smart_ptr(void); // create a pointer containing a *copy* of the object // this copy is taken because the pointer class maintains a dynamically allocated object // and the T& may not be (usually is not) dynamically allocated // constructor form smart_ptr(const T& data); // assignment form for an already-constructed smart-pointer smart_ptr& operator=(const T& data); // copy constructor implements counted referencing - no copy is made smart_ptr(const smart_ptr& r); // assignment of smart pointers implement counted referencing - no copy is made smart_ptr& operator=(const smart_ptr&); // create a pointer containing a dynamically created object // Note: the object must be allocated *by the user* with new // constructor form - must be called in the form smart_ptr x(new type(args)) explicit smart_ptr(T* data); // assignment form smart_ptr& operator= (T* data); // destructor decrements the reference count and delete only when the last reference is destroyed ~smart_ptr(void); ////////////////////////////////////////////////////////////////////////////// // logical tests to see if there is anything contained in the pointer since it can be null // there are two forms:explicit and implicit // implicit: if(!r) or if(r) // explicit: if(r.null()) or if(r.present()) operator bool(void) const; bool operator!(void) const; bool present(void) const; bool null(void) const; ////////////////////////////////////////////////////////////////////////////// // dereference operators and functions // dereference the smart pointer to get the object - use in the form *p1 T& operator*(void) throw(null_dereference); const T& operator*(void) const throw(null_dereference); // used as a prefix to a member access to the contained object e.g. p1->print() calls T::print() T* operator->(void) throw(null_dereference); const T* operator->(void) const throw(null_dereference); ////////////////////////////////////////////////////////////////////////////// // explicit function forms of the above assignment and dereference operators // set the value void set_value(const T& data); // get the value T& value(void) throw(null_dereference); const T& value(void) const throw(null_dereference); // set the pointer // deletes the previous pointer and adopts the passed pointer instead // Note: the object must be allocated *by the user* with new // Warning: it is very easy to break the memory management with this operation void set(T* data = 0); // get the pointer T* pointer(void); const T* pointer(void) const; ////////////////////////////////////////////////////////////////////////////// // functions to manage counted referencing // test whether two pointers point to the same object(known as aliasing the object) // used in the form if(a.aliases(b)) bool aliases(const smart_ptr&) const; // find the number of aliases - used when you need to know whether an object is still referred to from elsewhere (rare!) unsigned alias_count(void) const; // make this pointer unique with respect to any other references to the same object // if this pointer is already unique, it does nothing - otherwise it copies the object void make_unique(void); // delete the object and make the pointer null - does not make it unique first, so all other pointers to this will be null too void clear(void); // make the pointer unique and null in one step - does not affect other pointers that were pointing to the same object void clear_unique(void); // make this pointer a unique copy of the parameter // useful for expressions like p1.copy(p2) which makes p1 a pointer to a unique copy of the contents of p2 void copy(const smart_ptr&); // alternate form used in assignments: p1 = ps.copy() smart_ptr copy(void) const; // persistence functions void dump(dump_context& str) const throw(persistent_dump_failed); void restore(restore_context& str) throw(persistent_restore_failed); protected: smart_ptr_holder* m_holder; }; //////////////////////////////////////////////////////////////////////////////// // comparisons required for using this class in an STL container // These require == and < operator in the contained type T // the remaining relational operators are provided by template functions // a null pointer is less-than a non-null, two nulls are equal // these funcions are defined as non-members so that you only need provide // the underlying T::operator< and == if you are going to use these functions template bool operator==(const smart_ptr&, const smart_ptr&); template bool operator<(const smart_ptr&, const smart_ptr&); //////////////////////////////////////////////////////////////////////////////// // string/print utilities template std::string smart_ptr_to_string(const smart_ptr& ptr, std::string null_string); template otext& print_smart_ptr(otext& str, const smart_ptr& ptr, std::string null_string); template otext& print_smart_ptr(otext& str, const smart_ptr& ptr, unsigned indent, std::string null_string); //////////////////////////////////////////////////////////////////////////////// // persistence - call these rather than the methods // the dump routine dumps smart_ptr-specific information and then calls dump_pointer on the contents // similarly the restore routine calls restore_pointer // so therefore the class T should have non-member dump/restore functions template void dump_smart_ptr(dump_context& str, const smart_ptr& data) throw(persistent_dump_failed); template void restore_smart_ptr(restore_context& str, smart_ptr& data) throw(persistent_restore_failed); //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // Cloning smart pointer class for polymorphic class hierarchies // The contained class T should implement the clonable interface defined in clonable.hpp //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// template class smart_ptr_clone { public: ////////////////////////////////////////////////////////////////////////////// // member type definitions typedef T value_type; typedef T& reference; typedef const T& const_reference; ////////////////////////////////////////////////////////////////////////////// // constructors, assignments and destructors // create a null pointer smart_ptr_clone(void); // create a pointer containing a *copy* of the object // this copy is taken because the pointer class maintains a dynamically allocated object // and the T& may not be (usually is not) dynamically allocated // constructor form smart_ptr_clone(const T& data); // assignment form for an already-constructed smart-pointer smart_ptr_clone& operator=(const T& data); // copy constructor implements counted referencing - no copy is made smart_ptr_clone(const smart_ptr_clone& r); // assignment of smart pointers implement counted referencing - no copy is made smart_ptr_clone& operator=(const smart_ptr_clone&); // create a pointer containing a dynamically created object // Note: the object must be allocated *by the user* with new // constructor form - must be called in the form smart_ptr_clone x(new type(args)) explicit smart_ptr_clone(T* data); // assignment form smart_ptr_clone& operator= (T* data); // destructor decrements the reference count and delete only when the last reference is destroyed ~smart_ptr_clone(void); ////////////////////////////////////////////////////////////////////////////// // logical tests to see if there is anything contained in the pointer since it can be null // there are two forms:explicit and implicit // implicit: if(!r) or if(r) // explicit: if(r.null()) or if(r.present()) operator bool(void) const; bool operator!(void) const; bool present(void) const; bool null(void) const; ////////////////////////////////////////////////////////////////////////////// // dereference operators and functions // dereference the smart pointer to get the object - use in the form *p1 T& operator*(void) throw(null_dereference); const T& operator*(void) const throw(null_dereference); // used as a prefix to a member access to the contained object e.g. p1->print() calls T::print() T* operator->(void) throw(null_dereference); const T* operator->(void) const throw(null_dereference); ////////////////////////////////////////////////////////////////////////////// // explicit function forms of the above assignment and dereference operators // set the value void set_value(const T& data); // get the value T& value(void) throw(null_dereference); const T& value(void) const throw(null_dereference); // set the pointer // deletes the previous pointer and adopts the passed pointer instead // Note: the object must be allocated *by the user* with new // Warning: it is very easy to break the memory management with this operation void set(T* data = 0); // get the pointer T* pointer(void); const T* pointer(void) const; ////////////////////////////////////////////////////////////////////////////// // functions to manage counted referencing // test whether two pointers point to the same object(known as aliasing the object) // used in the form if(a.aliases(b)) bool aliases(const smart_ptr_clone&) const; // find the number of aliases - used when you need to know whether an object is still referred to from elsewhere (rare!) unsigned alias_count(void) const; // make this pointer unique with respect to any other references to the same object // if this pointer is already unique, it does nothing - otherwise it copies the object void make_unique(void); // delete the object and make the pointer null - does not make it unique first, so all other pointers to this will be null too void clear(void); // make the pointer unique and null in one step - does not affect other pointers that were pointing to the same object void clear_unique(void); // make this pointer a unique copy of the parameter // useful for expressions like p1.copy(p2) which makes p1 a pointer to a unique copy of the contents of p2 void copy(const smart_ptr_clone&); // alternate form used in assignments: p1 = ps.copy() smart_ptr_clone copy(void) const; // persistence functions void dump(dump_context& str) const throw(persistent_dump_failed); void restore(restore_context& str) throw(persistent_restore_failed); protected: smart_ptr_holder* m_holder; }; //////////////////////////////////////////////////////////////////////////////// // comparisons required for using this class in an STL container // These require == and < operator in the contained type T // the remaining relational operators are provided by template functions // a null pointer is less-than a non-null, two nulls are equal // these funcions are defined as non-members so that you only need provide // the underlying T::operator< and == if you are going to use these functions template bool operator==(const smart_ptr_clone&, const smart_ptr_clone&); template bool operator<(const smart_ptr_clone&, const smart_ptr_clone&); //////////////////////////////////////////////////////////////////////////////// // string/print utilities template std::string smart_ptr_clone_to_string(const smart_ptr_clone& ptr, std::string null_string); template otext& print_smart_ptr_clone(otext& str, const smart_ptr_clone& ptr, std::string null_string); template otext& print_smart_ptr_clone(otext& str, const smart_ptr_clone& ptr, unsigned indent, std::string null_string); //////////////////////////////////////////////////////////////////////////////// // persistence - call these rather than the methods // the dump routine dumps smart_ptr_clone-specific information and then calls dump_interface // similarly the restore routine calls restore_interface // so therefore the class T should implement the persistent interface defined by the class persistent in persistent.hpp template void dump_smart_ptr_clone(dump_context& str, const smart_ptr_clone& data) throw(persistent_dump_failed); template void restore_smart_ptr_clone(restore_context& str, smart_ptr_clone& data) throw(persistent_restore_failed); //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // No-copy smart pointer class for managing objects that cannot be copied //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// template class smart_ptr_nocopy { public: ////////////////////////////////////////////////////////////////////////////// // member type definitions typedef T value_type; typedef T& reference; typedef const T& const_reference; ////////////////////////////////////////////////////////////////////////////// // constructors, assignments and destructors // create a null pointer smart_ptr_nocopy(void); // copy constructor implements counted referencing - no copy is made smart_ptr_nocopy(const smart_ptr_nocopy& r); // assignment of smart pointers implement counted referencing - no copy is made smart_ptr_nocopy& operator=(const smart_ptr_nocopy&); // create a pointer containing a dynamically created object // Note: the object must be allocated *by the user* with new // constructor form - must be called in the form smart_ptr_nocopy x(new type(args)) explicit smart_ptr_nocopy(T* data); // assignment form smart_ptr_nocopy& operator= (T* data); // destructor decrements the reference count and delete only when the last reference is destroyed ~smart_ptr_nocopy(void); ////////////////////////////////////////////////////////////////////////////// // logical tests to see if there is anything contained in the pointer since it can be null // there are two forms:explicit and implicit // implicit: if(!r) or if(r) // explicit: if(r.null()) or if(r.present()) operator bool(void) const; bool operator!(void) const; bool present(void) const; bool null(void) const; ////////////////////////////////////////////////////////////////////////////// // dereference operators and functions // dereference the smart pointer to get the object - use in the form *p1 T& operator*(void) throw(null_dereference); const T& operator*(void) const throw(null_dereference); // used as a prefix to a member access to the contained object e.g. p1->print() calls T::print() T* operator->(void) throw(null_dereference); const T* operator->(void) const throw(null_dereference); ////////////////////////////////////////////////////////////////////////////// // explicit function forms of the above assignment and dereference operators // get the value T& value(void) throw(null_dereference); const T& value(void) const throw(null_dereference); // set the pointer // deletes the previous pointer and adopts the passed pointer instead // Note: the object must be allocated *by the user* with new // Warning: it is very easy to break the memory management with this operation void set(T* data = 0); // get the pointer T* pointer(void); const T* pointer(void) const; ////////////////////////////////////////////////////////////////////////////// // functions to manage counted referencing // test whether two pointers point to the same object(known as aliasing the object) // used in the form if(a.aliases(b)) bool aliases(const smart_ptr_nocopy&) const; // find the number of aliases - used when you need to know whether an object is still referred to from elsewhere (rare!) unsigned alias_count(void) const; // delete the object and make the pointer null - does not make it unique first, so all other pointers to this will be null too void clear(void); // make the pointer unique and null in one step - does not affect other pointers that were pointing to the same object void clear_unique(void); protected: smart_ptr_holder* m_holder; }; //////////////////////////////////////////////////////////////////////////////// // comparisons required for using this class in an STL container // These require == and < operator in the contained type T // the remaining relational operators are provided by template functions // a null pointer is less-than a non-null, two nulls are equal // these funcions are defined as non-members so that you only need provide // the underlying T::operator< and == if you are going to use these functions template bool operator==(const smart_ptr_nocopy&, const smart_ptr_nocopy&); template bool operator<(const smart_ptr_nocopy&, const smart_ptr_nocopy&); //////////////////////////////////////////////////////////////////////////////// // string/print utilities template std::string smart_ptr_nocopy_to_string(const smart_ptr_nocopy& ptr, std::string null_string); template otext& print_smart_ptr_nocopy(otext& str, const smart_ptr_nocopy& ptr, std::string null_string); template otext& print_smart_ptr_nocopy(otext& str, const smart_ptr_nocopy& ptr, unsigned indent, std::string null_string); //////////////////////////////////////////////////////////////////////////////// // there's no persistence on smart_ptr_nocopy because the whole point is that // it stores an uncopyable object and persistence is a form of copying //////////////////////////////////////////////////////////////////////////////// #include "smart_ptr.tpp" #endif