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

Обложка: Умный указатель на C++
2

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

На первый взгляд эта задача кажется довольно сложной, особенно если вы не эксперт в 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-компанию»

reaction
0
reaction
0
reaction
0
reaction
0
reaction
2
2
emoji