//  (C) Porkolab 2003
//
//  A.5.7.
//   
//  auto_ptr


#include <iostream>
#include <memory>

using namespace std;

/* define output operator for auto_ptr
 * - print object value or NULL
 */
template <class T>
ostream& operator<< (ostream& os, const auto_ptr<T>& p)
{
    // does p own an object ?
    if (p.get() == NULL) {
        os << "NULL";         // NO: print NULL
    }
    else {
        os << *p;             // YES: print the object
    }
    return os;
}

int main()
{
    auto_ptr<int> p(new int(42));
    auto_ptr<int> q;

    cout << "after initialization:" << endl;
    cout << " p: " << p << endl;
    cout << " q: " << q << endl;

    q = p;
    cout << "after assigning auto pointers:" << endl;
    cout << " p: " << p << endl;
    cout << " q: " << q << endl;

    *q += 13;                   // change value of the object q owns
    p = q;
    cout << "after change and reassignment:" << endl;
    cout << " p: " << p << endl;
    cout << " q: " << q << endl;
}

/*
after initialization:
 p: 42
 q: NULL
after assigning auto pointers:
 p: NULL
 q: 42
after change and reassignment:
 p: 55
 q: NULL
*/


namespace std {
    // auxiliary type to enable copies and assignments (now global)
    template<class Y>
    struct auto_ptr_ref {
        Y* yp;
        auto_ptr_ref (Y* rhs)
         : yp(rhs) {
        }
    };

    template<class T>
    class auto_ptr
    {
    private:
        T* ap;    // refers to the actual owned object (if any)
    public:
        typedef T element_type;

        // constructor
        explicit auto_ptr (T* ptr = 0) throw() : ap(ptr) { }

        // copy constructors (with implicit conversion)
        // - note: nonconstant parameter
        auto_ptr (auto_ptr& rhs) throw() : ap(rhs.release()) { }

        template<class Y>
        auto_ptr (auto_ptr<Y>& rhs) throw() : ap(rhs.release()) { }

        // assignments (with implicit conversion)
        // - note: nonconstant parameter
        auto_ptr& operator= (auto_ptr& rhs) throw()
        {
            reset(rhs.release());
            return *this;
        }
        template<class Y>
        auto_ptr& operator= (auto_ptr<Y>& rhs) throw()
        {
            reset(rhs.release());
            return *this;
        }

        // destructor
        ~auto_ptr() throw()
        {
            delete ap;
        }

        // value access
        T* get() const throw()
        {
            return ap;
        }
        T& operator*() const throw()
        {
            return *ap;
        }
        T* operator->() const throw()
        {
            return ap;
        }

        // release ownership
        T* release() throw()
        {
            T* tmp(ap);
            ap = 0;
            return tmp;
        }

        // reset value
        void reset (T* ptr=0) throw()
        {
            if (ap != ptr)
            {
                delete ap;
                ap = ptr;
            }
        }

        //  special conversions with auxiliary type 
        //  to enable copies and assignments
        auto_ptr(auto_ptr_ref<T> rhs) throw() : ap(rhs.yp)  { }

        auto_ptr& operator= (auto_ptr_ref<T> rhs) throw()
        {
             reset(rhs.yp);
             return *this;
        }
        template<class Y>
        operator auto_ptr_ref<Y>() throw()
        {
            return auto_ptr_ref<Y>(release());
        }
        template<class Y>
        operator auto_ptr<Y>() throw()
        {
            return auto_ptr<Y>(release());
        }
    };
}