Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Не підтверджено.doc
Скачиваний:
0
Добавлен:
01.04.2025
Размер:
3.08 Mб
Скачать
  1. Потенційні проблеми, які виникають при передачі об'єктів

Як зазначалося вище, об'єкти передаються функціям "за значенням", тобто за допомогою звичайного С++-механізму передачі параметрів, який теоретично за­хищає аргумент і ізолює його від параметра, що приймається. Незважаючи на ці обставини, тут все-таки можливий побічний ефект або навіть загроза для "життя" об'єкта, який використовується як аргумент. Наприклад, якщо оригінальний об'­єкт, який потім використовується як аргумент, вимагає динамічного виділення об­ласті пам'яті та звільняє цю пам'ять шляхом його руйнування, то локальна копія цього об'єкта під час виклику деструктора звільнить ту ж саму область пам'яті, яка була виділена оригінальному об'єкту. Поява такої ситуації стає потенційною про­блемою, оскільки оригінальний об'єкт все ще використовує цю (вже звільнену) область пам'яті. Описана ситуація робить оригінальний об'єкт "збитковим" і, по суті, непридатним для використання. Для розуміння сказаного вище розглянемо таку навчальну програму.

Код програми 3.9. Демонстрація механізму появи проблеми, яка виникає при

передачі об'єктів функціям, у яких динамічно виділяється та звільняється область пам'яті

#include <vcl>

#include <iostream> // Для потокового введення-виведення #include <conio> // Для консольного режиму роботи

using namespace std; // Використання стандартного простору імен

class myClass { // Оголошення класового типу

Int *р; public:

myClass(int с);

{р = new int; *р = с;

cout«"Виділення р-пам'яті звичайним конструктором" « endl;}

-myClassO;

{delete р; cout«"Звільнення р-пам'яті" « endl;} int Put() {return *p;}

ЧУ процесі виконання цієї функції якраз і виникає проблема, void Get(myClass obj) // Звичайна передача об'єкта

cout«"*р=" « obj.PutQ « endl;

nt mainO

myClass 0bjA(10); // Створення об'єкта класу

Get(ObjA); getchO; return 0;

}

Ось як виглядають результати виконання цієї програми.

Виділення р-пам'яті звичайним конструктором.

*р= 10

Звільнення р-пам'яті.

Звільнення р-пам'яті.

Ця програма містить принципову помилку, а саме: при створенні у функції mainO об'єкта ObjA виділяється область пам'яті, адреса якої присвоюється покажчи­ку ObjA.p. При передачі функції Get() об'єкт ObjA побітово копіюється в параметр obj. Це означає, що обидва об'єкти (ObjA і obj) матимуть однакове значення для покаж­чика р. Іншими словами, в обох об'єктах (в оригіналі та його копії) член даних р вказуватиме на одну і ту саму динамічно виділену область пам'яті. Після завер­шення роботи функції GetO об'єкт obj руйнується за допомогою деструктора. Дес­труктор звільняє область пам'яті, яка адресується покажчиком obj.p. Але ж ця (вже звільнена) область пам'яті - та ж сама область, на яку все ще вказує член даних (початкового об'єкта) ObjA.p! Тобто, як на перший погляд - виникає серйозна по­милка.

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

Одним із шляхів вирішення проблеми, пов'язаної з руйнуванням (ще потріб­них) даних деструктором об'єкта, який є параметром функції, полягає не в переда­чі самого об'єкта, а в передачі покажчика на нього або посилання. У цьому випад­ку копія об'єкта не створюється; отже, після завершення роботи функції деструк­тор не викликається. Ось як виглядає, наприклад, один із способів виправлення попереднього коду програми.

Код програми 3.10. Демонстрація механізму вирішення проблеми при передачі об'єктів функціям, у яких динамічно виділяється та звіль­няється область пам'яті

#include <vcl>

#include <iostream> // Для потокового введення-виведення

#include <conio> // Для консольного режиму роботи

using namespace std; // Використання стандартного простору імен

class myClass { // Оголошення класового типу

int *р;

public:

myClass(int с);

{р = new int; *р = с;

cout«"Виділення р-пам'яті звичайним конструктором" « endl;}

~myClass();

{delete р; cout«"Звільнення р-пам'яті" « endl;} int PutO {return *p;}

};

// Ця функція НЕ створює проблем.