C++. Умные указатели. Автоматический указатель. Класс auto_ptr

Умные указатели. Автоматический указатель. Класс auto_ptr

Перед использованием данной темы рекомендуется ознакомиться со следующей темой:


Содержание


Поиск на других ресурсах:

 
1. Необходимость в использовании умных указателей. Корректное создание и освобождение ресурса

В языке C++ работа с ресурсами (файлами, памятью) выполняется исходя из следующих утверждений. При выделении памяти под ресурс происходит инициализация. То есть, создается указатель, указывающий на ресурс. При завершении программы ресурс нужно освободить. В случае с файлом этот файл нужно закрыть. В случае выделенной памяти эту память необходимо освободить.

Однако возможна ситуация, когда процесс выполнения программы не дойдет до момента освобождения ресурса. Это может быть случай, когда при выполнении программы возникнет исключительная ситуация. В этом случае ресурс останется не освобожденным. Нижеследующий код демонстрирует эту ситуацию для файлового ресурса.

...

void main()
{
  // 1. Объявить указатель на ресурс
  FILE* fp; // Указатель на ресурс

  // 2. Попытка открытия файла
  if (!(fp = fopen("myFile.txt", "r")))
  {
    // если файл не открыт, то выход
    return;
  }

  // 3. Если файл открыт, то выполняется работа с файлом
  // ...

  // Здесь может возникнуть исключение и файл не будет закрыт
  // !!!

  // 4. Закрытие файла, если программа дойдет до этого файла
  fclose(fp);
}

В вышеприведенном коде в п.3 может возникнуть исключительная ситуация. В результате файл не будет закрыт. А это нежелательно.

Решение проблемы. Чтобы решить данную проблему, нужно создать специальный класс, который будет содержать следующие составляющие:

  • конструктор, в котором инициализируется ресурс. В нашем случае в конструкторе открывается файл;
  • деструктор, в котором ресурс освобождается. В нашем случае происходит закрытие файла.

После создания класса следует создать объект этого класса, в котором ресурс (файл) будет автоматически создаваться.
Если в программе возникнет исключение, произойдет освобождение объекта класса, что приведет к вызову деструктора и, соответственно, освобождению ресурса.

В наиболее упрощенном виде для файлового ресурса этот специальный класс имеет вид.

...

class InputFile
{
private:
  ifstream* p; // указатель на поток ввода
public:
  // Конструктор, в котором создается ресурс
  InputFile(const char* filename)
  {
    // Инициализация ресурса
    p = new ifstream(filename);
    if (!(*p))
    {
      return;
    }
  }

  // Деструктор
  ~InputFile()
  {
    p->close();
  }

  // Другие элементы класса
  // ...
};

По вышеприведенному образцу можно создавать собственные классы.

Подобные случаи могут встречаться и для операций с памятью, когда происходит ее выделение и освобождение. В языке C++ есть специальный класс auto_ptr (automatic pointer – автоматический указатель), обеспечивающий корректную работу с любыми указателями, указывающими на выделенный фрагмент памяти.

 

2. Класс auto_ptr. Автоматический указатель

Класс auto_ptr предназначен для работы с объектами, для которых требуется производить выделение и освобождение памяти с помощью оператора new. Класс размещается в пространстве имен std.

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

Объявление класса выглядит следующим образом

class std::auto_ptr<T>;

Здесь T – тип объекта, на который указывает указатель.

Создание объекта класса auto_ptr может выполняться одним из двух способов

auto_ptr<T> p;
auto_ptr<T> p(pT);

здесь

  • p – экземпляр типа автоматического указателя auto_ptr;
  • T – тип, на который указывает экземпляр p;
  • pT – указатель, указывающий на тип T. Этот указатель содержит значение адреса памяти, выделенной оператором new.

Класс auto_ptr содержит две функции:

T* get() const;
T* release();

здесь T – тип объекта, на который указывает указатель auto_ptr.

Метод get() возвращает указатель на объект типа T. Метод release() возвращает указатель на объект типа T, но забирает у указателя auto_ptr права владения на этот объект. Сам объект не уничтожается.

 

3. Пример, демонстрирующий использование указателя auto_ptr

Ниже приводится демонстрационное приложение, в котором используется указатель типа auto_ptr. В программе демонстрируется использование:

  • оператора = при присваивании указателей;
  • метода get();
  • метода release().

 

#include <iostream>
using namespace std;

void main()
{
  // Указатель типа auto_ptr
  // 1. Объявление указателя p1, который указывает на число 10
  auto_ptr<int> p1(new int(10));
  cout << "*p1 = " << *p1 << endl; // *p1 = 10

  // 2. Объявление указателя p2, который пока что равен nullptr
  auto_ptr<int> p2;

  // 3. Присваивание указателей.
  // При присваивании память выделена для p1 освобождается,
  // а для p2 выделяется или иными словами:
  // ресурс освобождается и ресурс выделяется
  p2 = p1; // после этого p1=nullptr, значение переходит в p2

  cout << "*p2 = " << *p2 << endl; // *p2 = 10
  // cout << *p1 << endl; // ошибка, потому что для p1 память уже освобождена

  // 4. Метод get() - вернуть обычный указатель
  // без передачи права владения
  int* pI1;
  pI1 = p2.get();
  cout << "*pI1 = " << *pI1 << endl; // 10
  cout << "*p2 = " << *p2 << endl; // 10, указатель p2 также указывает на 10

  // 5. Метод release() - получить указатель с передачей права владения
  int* pI2;
  pI2 = p2.release(); // pI2=>10, p2d=>nullptr
  cout << "*pI2 = " << *pI2 << endl; // 10

  // Проверка значения p2
  if (p2.get() == nullptr)
    cout << "p2==nullptr" << endl; // +
  else
    cout << "p2!=nullptr" << endl;
}

Результат выполнения программы

*p1 = 10
*p2 = 10
*pI1 = 10
*p2 = 10
*pI2 = 10
p2==nullptr

 


Связанные темы