template <class Type>
class SharedPtr {
private:
	Type* ptr_;
	int* count;

public:
	// Инициализируем счетчик ссылок в 1
	// (конструктор создает первый SharedPtr, который хранит указатель)
	explicit SharedPtr(Type *ptr = 0) {
		if (ptr != 0) count = new int(1);
		ptr_ = ptr;
	}

	// В деструкторе мы уменьшаем значение счетчика на 1,
	// если в объекте SharedPtr хранится ненулевой указатель
	// (мы удаляем один SharedPtr, который указывает на объект в куче)
	~SharedPtr() {
		if (ptr_ != 0) {
			(*count)--;
			if (*count == 0) {
				delete ptr_;
				delete count;
			}
		}
	}
	// В конструкторе копирования мы увеличиваем счетчик ссылок на 1,
	// если копируемый SharedPtr содержит ненулевой указатель
	// (конструктор копирования создает еще один SharedPtr с указателем на тот же самый объект)
	SharedPtr(const SharedPtr &ptr) {
		if (ptr.ptr_ != 0) {
			(*(ptr.count))++;
			count = ptr.count;
		}
		ptr_ = ptr.ptr_;
	}

	// Оператор присваивания уменьшает счетчик ссылок левого операнда на 1,
	// если внутри левого SharedPtr хранится ненулевой указатель,
	// увеличивает счетчик правого SharedPtr на 1,
	// если в правом SharedPtr хранится ненулевой указатель
	// (обычное дело для оператора присваивания — сначала освобождаем старые ресурсы,
	// потом выделяем новые,
	// но при этом нужно быть особенно внимательным с присваиванием самому себе).
	SharedPtr& operator=(const SharedPtr &ptr) {
		if (this != &ptr) {
			if (ptr_ != 0) {
				(*count)--;
				if (*(count) == 0) {
					delete ptr_;
					delete count;
				}
			}
			if (ptr.ptr_ != 0) {
				(*(ptr.count))++;
				count = ptr.count;
			}
			ptr_ = ptr.ptr_;
		}
		return *this;
	}

	//  Освобождаем старый указатель, и захватываем новый
	void reset(Type *ptr = 0) {
		if (ptr_ != 0) {
			(*count)--;
			if (*(count) == 0) {
				delete ptr_;
				delete count;
			}
		}
		ptr_ = ptr;
		if (ptr_ != 0) count = new int(1);
	}

	// Возвращает указатель, сохраненный внутри Shared
	// (например, чтобы передать его в какую-то функцию)
	Type* get() const {
		return ptr_;
	}

	Type& operator*() const {
		return *ptr_;
	}

	Type* operator->() const {
		return ptr_;
	}
};