Умный указатель на C++

Умный (интеллектуальный) указатель — это тот же обычный указатель, обеспечивающий безопасность благодаря автоматическому управлению памятью. Такой указатель помогает избежать множества проблем: «висячие» указатели, «утечки» памяти и отказы в выделении памяти. Интеллектуальный указатель должен подсчитывать количество ссылок на указанный объект.

На первый взгляд эта задача кажется довольно сложной, особенно если вы не эксперт в C++. Один из полезных подходов к решению — разделить задачу на две части: 1) обрисовать общий подход и создать псевдокод, а затем 2) написать подробный код.

Нам нужна переменная — счетчик ссылок, которая будет увеличиваться, как только мы добавляем новую ссылку на объект, и уменьшаться, когда мы ее удаляем. Наш псевдокод может иметь следующий вид:

template 
class SmartPointer {
	/* Класс интеллектуального указателя нуждается в указателях на собственно
	 * себя и на счетчик ссылок. Оба они должны быть указателями, а не реальным
	 * объектом или значением счетчика ссылок, так как цель интеллектуального
	 * указателя - в подсчете количества ссылок через множество интеллектуальных
	 * указателей н один объект */
	T * obj;
	unsigned * ref_count;
}

Для этого класса нам потребуется конструктор и деструктор, поэтому опишем их:

SmartPointer(T * object) {
	/* Мы хотим установить значение T * obj и установить счетчик
	 * ссылок в 1. */
}

SmartPointer(SmartPointer & sptr) {
	/* Этот конструктор создает новый интеллектуальный указатель на существующий
	 * объект. Нам нужно сперва установить obj и ref_count
	 * такими же, как в sptr. Затем, 
	 * поскольку мы создали новую ссылку на obj, нам нужно
	 * увеличить ref_count. */
}

~SmartPointer(SmartPointer sptr) {
	/* Уничтожаем ссылку на объект. Уменьшаем
	 * ref_count. Если ref_count = 0, освобождаем память и
	 * уничтожаем объект. */
}

Существует дополнительный способ создания ссылок — установка одного SmartPointer в другой. Нам понадобится переопределить оператор = для обработки этого случая, но сначала давайте сделаем набросок кода:

onSetEqals(SmartPointer ptr1, SmartPointer ptr2) {
	/* Если ptr1 имеет существующее значение, уменьшить его количество ссылок.
	* Затем копируем указатели obj и  ref_count. Наконец,
	* так как мы создали новую ссылку, нам нужно увеличить
	* ref_count. */
}

Осталось только написать код, а это дело техники:

template 
class SmartPointer {
public:
	SmartPointer(T * ptr) {
		ref = ptr;
		ref_count = (unsigned*)malloc(sizeof(unsigned));
		*ref_count = 1;
	}

	SmartPointer(SmartPointer & sptr) {
		ref = sptr.ref;
		ref_count = sptr.ref_count;
		++(*ref_count);
	}

	/* Перезаписываем оператор равенства (eqal), поэтому когда вы установите
	 * один интеллектуальный указатель в другой, количество ссылок старого указателя
	 * будет уменьшено, а нового - увеличено. 
	 */
	SmartPointer & operator=(SmartPointer & sptr) {
		/* Если уже присвоено объекту, удаляем одну ссылку. */
		if (*ref_count > 0) {
			remove();
		}
		if (this != &sptr) {
			ref = sptr.ref;
			ref_count = sptr.ref_count;
			++(*ref_count);
		}
		return *this;
	}

	~SmartPointer() {
		remove(); // удаляем одну ссылку на объект.
	}

	T operator*() {
		return *ref;
	}

	protected:
	void remove() {
		--(*ref_count);
		if (ref_count == 0) {
			delete ref;
			free(ref_count);
			ref = NULL;
			ref_count = NULL;
		}
	}

	T * ref;
	unsigned * ref_count;
}

Разбор задачи по книге «Карьера программиста. Как устроиться на работу в Google, Microsoft или другую ведущую IT-компанию»