Разработка класса реализующего «умный» указатель (smart pointer)
В данной теме приводится пример разработки шаблонного класса, реализующего «умный» указатель.
Содержание
- 1. Понятие «умного» указателя. Особенности реализации
- 2. Пример шаблонного класса «умного» указателя
- 3. Анализ программного кода
- Связанные темы
Поиск на других ресурсах:
1. Понятие «умного» указателя. Особенности реализации
Под понятием «умный» указатель подразумевается класс, имитирующий работу обычного указателя и расширяющий функциональность этого указателя путем внесения дополнительных возможностей. Примерами дополнительных возможностей могут служить:
- проверки на корректные пределы использования указателя;
- подсчет количества копий указателя;
- освобождение памяти, на которую указывает указатель и т.д.
Использование «умного» указателя делает невозможным случай попытки двойного освобождения того же участка памяти. Бывают случаи, когда на один участок памяти указывают два или более разных указателей. После освобождения этой памяти в одном из указателей попытка освобождения через другие указатели приведет к генерированию исключительной ситуации и «вылета» программы. Если в программе использовать специально разработанный класс «умного» указателя, эта ситуация будет обработана корректно.
С точки зрения реализации в классе, для «умного» указателя определяют следующие внутренние данные (поля):
- указатель на объект заданного типа. Если это обобщенный класс, то указатель на объект обобщенного типа T;
- счетчик количества обращений к объекту.
Для обеспечения минимальных возможностей «умного» указателя в классе нужно реализовать как минимум одну операторную функцию operator->(), которая обеспечивает доступ по указателю.
⇑
2. Пример шаблонного класса «умного» указателя
Ниже представлен программный код двух классов:
- класс Int, реализующий некоторое целое число. Этот класс разработан с целью демонстрации. По желанию можно использовать любой другой класс;
- шаблонный класс SmartPtr, реализующий «умный» указатель.
#include <iostream> using namespace std; // Тема: умные указатели // Задан некоторый класс class Int { private: int d; public: // Конструктор Int(int _d) : d(_d) { } // Методы доступа int Get() { return d; } void Set(int _d) { d = _d; } // Метод Print() void Print(string msg) { cout << msg.c_str() << d << endl; } }; // Класс умного указателя на обобщенный тип T template <typename T> class SmartPtr { private: T* p; // указатель на обобщенный тип T int count; // количество копий public: // Конструктор SmartPtr(T* _p = nullptr) { // Записываем 0 - копий нет count = 0; p = _p; } // Конструктор копирования SmartPtr(const SmartPtr& obj) { // Создается копия p = obj.p; // Увеличить счетчик count++; } // Оператор копирования SmartPtr operator=(const SmartPtr& obj) { // Создается копия p = obj.p; count++; return *this; } // Деструктор - уничтожает объект-оригинал, // объекты-копии не уничтожаются. ~SmartPtr() { // Если есть объект и нету копий, // то просто уничтожаем это объект. if ((p != nullptr) && (count == 0)) { cout << "Delete object" << endl; delete[] p; } else { // иначе, просто уничтожается копия cout << "Delete copy" << endl; count--; // уменьшить счетчик копий } } // Переопределить оператор -> доступа по указателю T* operator->() { return p; } }; void main() { // 1. Создать объект класса Int Int* obj1 = new Int(10); obj1->Print("obj1: "); // 2. Инициализировать этим объектом умный указатель SmartPtr<Int> ptr(obj1); ptr->Print("ptr->obj: "); // 3. Создать копию умного указателя SmartPtr<Int> ptr2 = ptr; // вызывается конструктор копирования ptr2->Print("ptr2->obj: "); // 4. Создать еще одну копию умного указателя SmartPtr<Int> ptr3; ptr3 = ptr2; // вызывается оператор копирования ptr3->Print("ptr3->obj: "); }
После запуска на выполнения программа выдаст следующий результат
obj1: 10 ptr->obj: 10 ptr2->obj: 10 ptr3->obj: 10
⇑
3. Анализ программного кода
Шаблонный класс SmartPtr<T> оперирует обобщенным типом T. В классе объявляется конструктор, принимающий указатель на тип T. При создании первого экземпляра, внутренняя переменная count (количество копий) устанавливается равной в значение 0. Приращение этой переменной возможно в случаях когда создается копия указателя. А копия может создаваться при вызове конструктора или оператора копирования. Таким образом, отслеживается количество созданных копий «умного» указателя.
Наиболее важным элементом класса является деструктор
~SmartPtr() { // Если есть объект и нету копий, // то просто уничтожаем этот объект. if ((p != nullptr) && (count == 0)) { delete[] p; } else { // иначе, просто уничтожается копия count--; // уменьшить счетчик копий } }
Как видно из кода деструктора, память для указателя освобождается только один раз вне зависимости от количества копий. Фактически, освобождается память для указателя, память для которого была выделена в первый раз. Если рассматривается копия, то освобождения памяти не происходит, уменьшается только счетчик копий. Деструктор обеспечивает правильное освобождение памяти в случае, если есть копии «умного» указателя.
⇑
Связанные темы
- Общие понятия. Типы указателей. Управляемые и неуправляемые указатели. Указатели на функцию. Примеры использования
- Понятие класса. Объявление класса. Объект класса. Классы в среде CLR. Инкапсуляция данных в классе
⇑