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, p2=>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

 


Споріднені теми