// // aegis - project change supervisor // Copyright (C) 2007, 2008 Peter Miller // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see // . // #ifndef COMMON_AC_SHARED_PTR_AEGIS_H #define COMMON_AC_SHARED_PTR_AEGIS_H #include // for assert /** * The aegis_shared_ptr class is used to represent a smart pointer with * reference counted copy semantics. The object pointed to is deleted * when the last aegis_shared_ptr pointing to it is destroyed or reset. * * This class is only used of std::tr1::shared_ptr and * boost::shared_ptr are both unavailable. It only implements as much * of a shared_ptr as the Aegis code actually uses. */ template class aegis_shared_ptr { private: /** * The reference_count instance variable is used to remember the * location of the reference count. By having the reference count * separate from the subject, we can cope with compatible pointers, * not just exact pointers. * * This is not ideal because it allocates huge numbers of small * objects. Some heap implementations go slowly when faced with * many small allocations. Some heap implementations waste a lot * of memory when faced with many small allocations. */ long *reference_count; /** * The subject instance variable is used to remember the location * of the object being reference counted. By having it separate * from the reference count, we can skip one indirection. */ T *subject; public: /** * The valid method is used to establish the correctness of the * internal state. Think of it as the contract. */ bool valid() const { return ( (reference_count != 0) ? (*reference_count > 0 && subject != 0) : (subject == 0) ); } /** * The destructor. * * @Note * This destructor is not virtual. Thou shalt not derive from * this class. */ ~aegis_shared_ptr() { assert(valid()); if (reference_count) { --*reference_count; if (*reference_count <= 0) { delete subject; subject = 0; delete reference_count; reference_count = 0; } } } /** * Swap the contents of two aegis_shared_ptr objects. * This method swaps the internal pointers to T. This can be done * safely without involving a reference / unreference cycle and is * therefore highly efficient. */ void swap(aegis_shared_ptr &rhs) { assert(valid()); assert(rhs.valid()); long *temp_reference_count = reference_count; T *temp_subject = subject; reference_count = rhs.reference_count; subject = rhs.subject; rhs.reference_count = temp_reference_count; rhs.subject = temp_subject; } /** * The reset method is used to drop the reference, if there is one. */ void reset() { // swap with a NULL pointer aegis_shared_ptr tmp; this->swap(tmp); } /** * The default constructor. */ aegis_shared_ptr() : reference_count(0), subject(0) { assert(valid()); } /** * The constructor. It permits initialization from any compatable * pointer. Y must be a complete type. * * @param rhs * The object pointed to, the object to be managed. */ template explicit aegis_shared_ptr(Y *rhs) : reference_count(0), subject(0) { assert(valid()); if (rhs) { reference_count = new long(1); subject = rhs; } assert(valid()); } /** * The copy constructor. * * @param rhs * The smart pointer to be copied. */ aegis_shared_ptr(const aegis_shared_ptr &rhs) : reference_count(0), subject(0) { assert(rhs.valid()); if (rhs.subject) { assert(rhs.reference_count); reference_count = rhs.reference_count; subject = rhs.subject; ++*reference_count; } assert(valid()); } template friend class aegis_shared_ptr; /** * The compatible copy constructor. * * @param rhs * The compatable smart pointer to be copied. */ template aegis_shared_ptr(const aegis_shared_ptr &rhs) : reference_count(0), subject(0) { assert(rhs.valid()); if (rhs.subject) { assert(rhs.reference_count); reference_count = rhs.reference_count; subject = rhs.subject; ++*reference_count; } assert(valid()); } /** * The assignment operator. * * @param rhs * The right hand side of the assigment, the pointer to copy. */ aegis_shared_ptr & operator=(const aegis_shared_ptr &rhs) { assert(valid()); assert(rhs.valid()); if (this != &rhs) { // In case you haven't seen the swap() technique to // implement copy assignment before, here's what it does: // // 1) Create a temporary aegis_shared_ptr<> instance via the // copy constructor, thereby increasing the reference // count of the source object. // // 2) Swap the internal object pointers of *this and the // temporary aegis_shared_ptr<>. After this step, *this // already contains the new pointer, and the old pointer // is now managed by temp. // // 3) The destructor of temp is executed, thereby // unreferencing the old object pointer. // // This technique is described in Herb Sutter's "Exceptional // C++", and has a number of advantages over conventional // approaches: // // - Code reuse by calling the copy constructor. // - Strong exception safety for free. // - Self assignment is handled implicitly. // - Simplicity. // - It just works and is hard to get wrong; i.e. you can // use it without even thinking about it to implement // copy assignment where ever the object data is managed // indirectly via a pointer, which is very common. // aegis_shared_ptr temp(rhs); this->swap(temp); assert(valid()); assert(rhs.valid()); } return *this; } /** * The assignment operator. * * @param rhs * The right hand side of the assigment, the pointer to copy. */ template aegis_shared_ptr & operator=(const aegis_shared_ptr &rhs) { assert(valid()); assert(rhs.valid()); if (this != (aegis_shared_ptr *)&rhs) { aegis_shared_ptr tmp(rhs); this->swap(tmp); assert(valid()); assert(rhs.valid()); } return *this; } #if 0 /** * The constructor * * @param rhs * The pointer we are to manage. */ template explicit aegis_shared_ptr(std::auto_ptr &rhs) : reference_count(0), subject(0) { Y *tmp = r.get(); if (tmp) { reference_count = new long(1); subject = tmp; } assert(valid()); } /** * the assignment operator. * * @param rhs * The pointer we are to manage. */ template aegis_shared_ptr & operator=(std::auto_ptr &rhs) { assert(valid()); reset(); if (tmp) { reference_count = new long(1); subject = tmp; } assert(valid()); return *this; } #endif /** * The dereference operator. Return a reference to the object, not * to the smart pointer. * * This is part of why it's called a "smart pointer" when it's * actually neither - it *acts* like a pointer. */ T & operator*() const { assert(subject != 0); return *subject; } /** * The "pointing at" operator. Return a pointer to the object, not * to the smart pointer. * * This is part of why it's called a "smart pointer" when it's * actually neither - it *acts* like a pointer. */ T * operator->() const { assert(subject != 0); return subject; } /** * The get method is used to obtain the pointer to the object. */ T * get() const { return subject; } /** * The bool operator, for implicit conversion to bool. * Test of the pointers not NULL. */ operator bool() const { return (subject != 0); } /** * The logical not operator. * Test if the pointer is NULL. */ bool operator!() const { return (subject == 0); } inline bool operator==(const aegis_shared_ptr &rhs) const { return (subject == rhs.get()); } template inline bool operator==(const aegis_shared_ptr &rhs) const { return (subject == rhs.get()); } inline bool operator!=(aegis_shared_ptr &rhs) const { return (subject != rhs.get()); } template inline bool operator!=(aegis_shared_ptr &rhs) const { return (subject != rhs.get()); } inline bool operator<(aegis_shared_ptr &rhs) const { return (subject < rhs.get()); } template inline bool operator<(aegis_shared_ptr &rhs) const { return (subject < rhs.get()); } }; #endif // COMMON_AC_SHARED_PTR_AEGIS_H