#include <iostream>


template<class T>
class Auto_ptr{
public:
	// constructors
    explicit Auto_ptr(T* p = NULL): value(p) { };	// constructor
	Auto_ptr(Auto_ptr& p);						// copy constructor
	Auto_ptr& operator= (const Auto_ptr& p);		    // copy assignment
	~Auto_ptr() { std::cout << "pointer deleted\n"; delete value; }					    // destructor

	// access operators
	const T& operator* () const { return *value; }		// dereference operator
	const T* operator->() const { return value;	}		// indirect class member access (arrow) operator

	// non-modifying members
	T* get() { return value; }							// getter method
	void reset(T* v);							// reassing new value(default value: nullptr)
	T* release();										// transfers the object to another pointer; without destroying it
private:
	// data member
	T* value;
	
};

//--------------------------------------------------------------------------------------------------------
// class Auto_ptr member implementations
// Constructors
// copy constructor
template<class T>
Auto_ptr<T>::Auto_ptr(Auto_ptr& p) {
	value = p.release();
}

// copy assignment
template<class T>
Auto_ptr<T>& Auto_ptr<T>::operator= (const Auto_ptr& p ) {
	if (this == &p) return *this;
	if (value) delete value;
	value = p.value;
	return *this;
}

/*
	Function: release()
	Use: T ptr =  auto_ptr_obj.release();

	It transfers the pointer value to the
	caller, setting the data member value
	to nullptr.
*/
template <class T>
T* Auto_ptr<T>::release() {
	T* temp = value;
	value = NULL;
	return temp;
}

/*
	Function: reset()
	Use: auto_ptr_obj.release(new_pointer);

	It deletes the object pointer to by
	pointer value and assings new_pointer; 
*/
template <class T>
void Auto_ptr<T>::reset(T* v) {
	delete value;
	value = v;
}	


void test1 () {
	std::cout <<"\nTest constructor and get() member.\n";
	Auto_ptr<int> p(new int);
	*p.get() = 5;
	std::cout <<"p points to: "<< *p << "\n";

	//assert(*p, 5);
	std::cout <<"TEST 1 DONE\n";
}

void test2 () {
	std::cout <<"\nTest reset() and release() members.\n";
	Auto_ptr<int> p(new int);
	*p.get() = 5;
	std::cout <<"p points to: "<< *p << "\n";

	p.reset(new int(10));
	std::cout <<"reset() p points to: "<< *p << "\n";
	//assert(*p, 10);

	int *temp = p.release();
	std::cout <<"caller of release(), temp points to: "<< *temp << "\n";
	//assert(*temp, 10);

	// nullptr dereferece error 
	// std::cout <<"p after being release()d points to: "<< *p << "\n";

	std::cout <<"TEST 2 DONE\n";
}

void test3 () {
	std::cout <<"\nTest copy constructor and copy assignment.\n";
	Auto_ptr<int> p1(new int(10));
	Auto_ptr<int> p2(p1);
	
	std::cout <<"copy constructed p2 points to: "<< *p2 << "\n";
	//assert(*p2, 10);

	Auto_ptr<int> p3(new int(20));
	p1 = p3;
	std::cout <<"copy assigned p1 points to: "<< *p1 << "\n";
	//assert(*p1, 20);

	std::cout <<"TEST 3 DONE\n";
}


int main () {
	test1 ();
	test2 ();
	test3 ();

 
}