Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
+ООП_Навч_посібник.doc
Скачиваний:
7
Добавлен:
01.07.2025
Размер:
6.58 Mб
Скачать

13.6. Повернення об'єктів функціями

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

Код програми 13.11. Демонстрація механізму повернення об'єкта функцією

#include <vcl>

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

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

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

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

char sMas[80];

public:

void setStr(char *str){ strcpy(sMas, str);}

void showStr() { cout << "Рядок: " << sMas << "\n";}

};

// Ця функція повертає об'єкт типу strClass.

strClass input()

{

char strMas[80];

strClass s_ob;

cout << "Введіть рядок: "; cin >> strMas;

s_ob.setStr(strMas);

return s_ob;

}

int main()

{

strClass S_ob; // Створення об'єкта класу

// Присвоюємо об'єкт, повернутий функцією input(), об'єкту S_ob.

S_ob = input();

S_ob.showStr();

getch(); return 0;

}

У наведеному прикладі функція input() створює локальний об'єкт s_ob класу strClass, а потім зчитує рядок з клавіатури. Цей рядок копіюється в рядок s_ob.sMas, після чого об'єкт s_ob повертається функцією input() і присвоюється об'єкту S_ob у функції main().

Потенційна проблема під час повернення об'єктів функціями

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

Код програми 13.12. Демонстрація виникнення помилки, що виникає під час повернення об'єкта функцією

#include <vcl>

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

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

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

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

char *s;

public:

strClass() { s = 0;}

~strClass() { if(s) delete[]s; cout << "Звільнення s-пам'яті.\n";}

void setStr(char *str); // Завантаження рядка.

{ s = new char[strlen(str)+1]; strcpy(s, str); }

void showStr() { cout << "s= " << s << "\n";}

};

// Ця функція повертає об'єкт типу strClass.

strClass input()

{

char strMas[80];

strClass s_ob;

cout << "Введіть рядок: "; cin >> strMas;

s_ob.setStr(strMas);

return s_ob;

}

int main()

{

strClass S_ob; // Створення об'єкта класу

// Присвоюємо об'єкт, повернутий функцією input(), об'єкту S_ob.

S_ob = input(); // Ця настанова генерує помилку!!!!

S_ob.showStr(); // Відображення "сміття".

getch(); return 0;

}

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

Введіть рядок: Привіт

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

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

s= тут появиться сміття

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

Звернемо Вашу увагу на те, що виклик деструктора класу strClass відбувається тричі! Вперше він викликається при виході локального об'єкта s_ob з області видимості у момент його повернення з функції input(). Другий виклик деструкторa ~strClass() відбувається тоді, коли руйнується тимчасовий об'єкт, який повертається функцією input(). Коли функція повертає об'єкт, то автоматично генерується невидимий (для Вас) тимчасовий об'єкт, який зберігає повернуте значення. У нашому випадку цей об'єкт просто є побітовою копією об'єкта s_ob, який міститиме значення, що повертається з функції. Отже, після повернення з функції виконується деструктор тимчасового об'єкта. Оскільки область пам'яті, що виділяється для зберігання рядка, який вводить користувач, вже була звільнена (причому двічі!), то під час виклику функції showStr() на екран виведеться "сміття"1. Нарешті, після завершення роботи програми викликається деструктор об'єкта S_ob (у функції main()). Ситуація тут ускладнюється тим, що під час першого виклику деструктора звільняється пам'ять, виділена для зберігання рядка, отримуваного функцією input(). Таким чином, у цій ситуації погано не тільки те, що решта два звернення до деструктора класу strClass спробують звільнити вже звільнену динамічно виділену область пам'яті, але вони також можуть зруйнувати систему динамічного розподілу пам'яті.

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