69734

Перевантаження операцій new і delete

Домашняя работа

Информатика, кибернетика и программирование

Поведінка перевантажених операцій повинна відповідати діям, які виконуються ними за замовчуванням. Для операції new це означає, що вона повинна повертати правильне значення, коректно обробляти запит на виділення пам’яті нульового розміру і породжувати виключення при неможливості...

Украинкский

2014-10-09

50.5 KB

0 чел.

Самостійне вивчення

Тема 11: Перевантаження операцій new і delete

Щоб забезпечити альтернативні варіанти управління пам'яттю, можна визначати власні варіанти операцій new і new[] для виділення динамічної пам'яті під об'єкт і масив об'єктів відповідно, а також операції delete і delete[] для її звільнення.

Ці функції-операції повинні відповідати наступним правилам:

  1.  їм не вимагається передавати параметр типу класу;
  2.  першим параметром функціям new і new[] повинен передаватися розмір объекта типу size_t (це тип, який повертається операцією sizeof, він визначається в заголовочному файлі <stddef.h>); при виклику він передається у функції неявним чином;
  3.  вони повинні визначатися з типом значення void*, який повертається, навіть якщо return повертає покажчик на інші типи (частіше всього на клас);
  4.  операція delete повинна мати тип повернення void і перший аргумент типу
  5.  void*;
  6.  операції виділення і звільнення пам'яті є статичними элементами класу.

Поведінка перевантажених операцій повинна відповідати діям, які виконуються ними за замовчуванням. Для операції new це означає, що вона повинна повертати правильне значення, коректно обробляти запит на виділення пам'яті нульового розміру і породжувати виключення при неможливості виконання запиту

Для операції delete слід дотримувати умову, що видалення нульового покажчика повинне бути безпечним, тому всередині операції необхідна перевірка покажчика на нуль і відсутність яких-небудь дій у разі рівності.

Стандартні операції виділення і звільнення пам'яті можуть використовуватися в області дії класу разом з перевантаженими (за допомогою операції доступа до області видимості :: для об'єктів цього класу і безпосередньо — для будь-кого інших).

Перевантаження операції виділення пам'яті застосовується для економії пам'яті, підвищення швидкодії програми або для розміщення даних в деякій конкретній області. Наприклад, нехай описується клас, який містить покажчик на деякий об'єкт:

class Obj {...};

class pObj{

private:

Obj *p;

};

При виділенні пам'яті під об'єкт типу pObj за допомогою стандартної операції new

pObj *р = new pObj;

фактична кількість байтів перевищуватиме sizeof(pObj), оскільки new звичайно записує в початок області, яка виділяється, її розмір (для того, щоб правильно відпрацьовувала операція delete). Для невеликих об'єктів ці невигідні витрати можуть виявитися вельми впливовими. Для економії пам'яті можна написати власну операцію new класу pObj, яка виділятиме великий блок пам'яті, а потім розміщуватиме в ньому покажчики на Obj. Для цього в об'єкт pObj вводиться статичне поле headOfFree, в якому зберігається покажчик на першу вільну комірку блоку для розміщення чергового об'єкту. Комірки які не використовуються зв'язуються в список. Щоб не позичати пам'ять під поле зв'язку, використовується об'єднання (union), за допомогою якого одна і таж комірка використовується або для розміщення покажчика на об'єкт, або для зв'язку з наступною вільною коміркою:

class pObj

{

public:

static void * operator new(size_t size);

....

private:

union

{

Obj *p;           // Покажчик на об'єкт

pObj *next;   // Покажчик на наступну вільну комірку

};

static const int BLOCK_SIZE; // Розмір блоку

// Заголовок списку вільних комірок:

static pObj *headOfFree;

};

void * pObj::operator new(size_t size)

{

// Перенаправити запити невірної кількості пам'яті

// стандартної операції new:

if (size != sizeof(pObj)) return ::operator new(size);

pObj *p  headOfFree; // Покажчик на першу вільну комірку

// Перемістити покажчик списку вільних комірок:

if (p) headOfFree = р -> next;

// Якщо вільної пам'яті немає, виділяємо черговий блок:

else {

pObj *newblock = static_cast<pObj*>

(::operator new(BLOCK_SIZE * sizeof(pObj)));

// Всі комірки вільні, окрім першої (вона буде

// зайнята), зв'язуємо їх:

for (int i=1; і<BLOCK_SIZE-1;++j) newblock[i].next = &newblock[i+1];

newblock[BLOCK_SIZE - l].next = 0;

// Встановлюємо початок списку вільних комірок:

headOfFree = &newblock[l];

р = newblock;

} return p; // Повертаємо покажчик на виділену пам'ять

}

Перевантажена операція new успадковується, тому вона викликається для похідних об'єктів. Якщо їх розмір не відповідає розміру базового (а так, скорше всього, і є), це може викликати проблеми. Щоб їх уникнути, на початку операції перевіряється відповідність розмірів. Якщо розмір об'єкту не рівний тому, для якого перевантажена операція new, запит на виділення пам'яті передається стандартній операції new.

В програмі, яка використовує клас pObj, повинна бути присутня ініціалізація його статичних полів

pObj *pObj::headOfFree;    // Встановлюється в 0 за замовчуванням

const int pObj::BLOCK_SIZE = 1024;

Як видно з цього прикладу, крім економії пам'яті досягається ще і висока швидкодія, адже в більшості випадків виділення пам'яті зводиться до декількох простих операторів.

Природно, що якщо операція new перевантажена, то ж саме повинне бути зроблено і для операції delete (наприклад, в нашому випадку стандартна операція delete не знайде на початку об'єкту вірної інформації про його розміри що приведе до невизначеної поведінки програми).

В розглянутому прикладі операція delete повинна додавати звільнений елемент пам'яті до списку вільних комірок:

void pObj::operator delete(void * ObjToDie. size_t  size)

{

if (ObjToDie == 0) return;

if (size != sizeof(pObj))

{

::operator delete(ObjToDie);

return;

}

pObj *p= static_cast<pObj*>(ObjToDie);

p->next = headOfFree;

headOfFree = p;

}

В операції delete виконана перевірка відповідності розмірів об'єктів, аналогічна приведеної в операції new.

Перевантаження операції приведення типу

Можна визначити функції-операції, які здійснюватимуть перетворення об'єкту класу до іншого типу. Формат:

operator ім'я_нового_типу();

Тип значення, яке повертається, і параметри вказувати не вимагається. Можна визначати віртуальні функції перетворення типу. Приклад:

monstr::operator int(){return health;}

monstr Vasia;

cout << int(Vasia);

Перевантаження операції виклику функції

Клас, в якому визначена операція виклику функції, називається функціональним. Від такого класу не вимагається наявності

інших полів і методів:

class if_greater

{

public:

int operator() (int а, int b) const

{

return а > b;

}

};

Використання такого класу має вельми специфічний синтаксис. Наприклад:

if_greater x;

cout << х(1,5)<< endl;                       // Результат - 0

cout << if_greater()(5,1) << endl;     // Результат - 1

Оскільки в класі if_greater визначена операція виклику функції з двома параметрами, вираз х(1,5) є допустимим (те ж саме можна записати у вигляді х.operator()(1, 5)). Як видно з прикладу об'єкт функціонального класу використовується так, якби він був функцією.

В другому операторі виведення вираз if_greater() використовується для виклику конструктора за замовчуванням класу if_greater.

Результатом виконання цього виразу є об'єкт класу if_greater. Далі, як і в попередньому випадку, для цього об'єкту викликається функція з двома аргументами, записаними в круглих дужках.

Операцію () можна визначати тільки як метод класу. Можна визначити перевантажені операції виклику функції з різною кількістю аргументів. Функціональні об'єкти широко застосовуються в стандартній бібліотеці C++.

Перевантаження операції індексації

Операція індексації [ ] звичайно перевантажується, коли тип класу представляє безліч значень, для якої індексація має сенс. Операція індексації повинна повертати посилання на елемент, який міститься в множині.Приклад класу Vect, призначеного для зберігання массива цілих чисел і безпечної роботи з ним:

#iinclude <iostream.h>

#iinclude <stdlib.h>

class Vect

{

public:

explicit Vect(int n = 10);

Vect(const int а[ ], int n); //ініціалізація масивом

~Vect() { delete [ ] p; }

int &operator [ ](int i);

void Print();

private:

int *p;

int size;

};

Vect::Vect(int n):Size(n)

{

p = new int[size];

};

Vect::Vect(const int a[], int n) : size(n)

{

p = new int[size];

for (int і = 0; і < size; i++)

p[i] - а[i];

}

// Перевантаження операції індексації:

int& Vect::operator [] (int i)

{

if(i < 0 || і >= size)

{

cout << "Невірний індекс (і = " << і << ")" << endl;

cout << "Завершення програми" << endl;

exit(0);

};

return p[i];

};

void Vect::Print()

{

for (int і = 0; і < size; i++)

cout << p[i]<< " "; cout << endl;

}

int main()

{

int arr[10]= {1, 2, 3, 4,  5, 6,  7, 8.  9,  10};

Vect а(arr,10);

а.Print();

cout << а[5]<< endl;

cout << а[12]<< endl;

return 0;

}

Результат роботи програми:

1 2 3 4 5 6 7 8 9 10

6

Невірний індекс (і = 12)

Завершення програми

Перевантажена операція індексації одержує цілий аргумент і перевіряє, чи лежить його значення в межах діапазону масиву. Якщо так, то повертається адрес елемента, що відповідає семантиці стандартної операції індексації.

В даному прикладі конструктор з параметром за замовчуванням оголошений як explicit для того, щоб він не був конструктором перетворення типу який викликається неявно. Ключове слово explicit вказує на те, що цей конструктор викликатиметься тільки явним чином. Операцію [] можна визначати тільки як метод класу.


 

А также другие работы, которые могут Вас заинтересовать

6237. ВИЧ-инфекция (HIV infection). Эпидемиология. Клиническая картина. СПИД 128.19 KB
  ВИЧ-инфекция (HIV infection). Эпидемиология. Клиническая картина. ВИЧ-инфекция-антропонозное вирусное заболевание, в основе патогенеза которого лежит прогрессирующий иммунодефицит и развитие вследствие этого вторичных оппо...
6238. Рынок: спрос, предложение и цена 295.5 KB
  Рынок: спрос, предложение и цена Совокупность отношений товарного обмена, устанавливающих непосредственные связи между производителями и потребителями, называется рынком. Основными параметрами рынка являются: спрос, предложение и цена. Теория спроса...
6239. Характеристика немецкой классической философии, гносеология, этика и эстетика И. Канта 108 KB
  Характеристика немецкой классической философии, гносеология, этика и эстетика И. Канта. Вопрос 1 Характеристика немецкой классической философии, теории И. Канта эволюции вселенной, суждений и категорий науки. Рассудок не черпает свои з...
6240. Общая характеристика методов прогнозирования 204.5 KB
  Характеристика методов и подходов к построению прогнозов Существуют два подхода к построению прогнозов: первый количественный анализ, второй - качественный анализ. Количественный подход основан на различных математических моделях, испол...
6241. Подшипники скольжения открытого типа 229.91 KB
  Подшипники скольжения открытого типа Подшипники прокатных валков являются ответственными узлами рабочей клети, от их конструкции и правильной эксплуатации зависит бесперебойная работа прокатного стана. Подшипники работают в очень тяжелых условиях...
6242. Фармакопейный анализ кислоты хлористоводородной и галогенидов щелочных металлов 89 KB
  Фармакопейный анализ кислоты хлористоводородной и галогенидов щелочных металлов Описание. Бесцветные прозрачные жидкости со своеобразным запахом, кислой реакции. Кислота хлористоводородная - летучая жидкость. Свойства препаратов кислоты соляной...
6243. Генетика как научный фундамент биотехнологии 93.5 KB
  Генетика как научный фундамент биотехнологии Основы биотехнологии. Задачи биотехнологии. Структура современной биотехнологии Клеточная инженерия: достижения и перспективы Генная инженерия: достижения и перспективы Генетические основы выс...
6244. Рынок капитала, рынок земли 80.5 KB
  Рынок капитала, рынок земли. 1. Понятие капитала и процента. 2. Долгосрочные инвестиции. Анализ эффективности инвестиций. 3. Рынок земельных ресурсов и земельная рента. 4. Прибыль как факторный доход. 1. Капитал - это определенная сумма благ в ...
6245. Язык программирования Паскаль 73.5 KB
  Язык программирования Паскаль План Основные сведения Основные элементы Паскаля Структура программы Организация и описание данных Основные сведения Язык Паскаль является одним из самых распространенных в настоящее время алгори...