Міністерство освіти і науки України
Національний університет „Львівська політехніка”
Кафедра ЕОМ
/
Звіт
про виконання лабораторної роботи №3
з дисципліни:
“Програмування, частина 2 (Об’єктно-орієнтоване програмування)”
на тему:“ Засоби роботи з динамічною пам’яттю. Динамічні масиви ”
2017
Мета: познайомитися із динамічними масивами.
Теоретичні відомості
Динамічне виділення пам’яті
В С++ об’єкти можна розміщати статично – під час компіляції, або динамічно – під час виконання програми, шляхом виклику функцій зі стандартної бібліотеки. Основна відмінність у використанні даних методів – в їхній ефективності та гнучкості. Статичне розміщення більш ефективне, так як виділення пам’яті відбувається до виконання програми, проте воно менш гнучке, тому що необхідно наперед знати тип і розмір об’єкту. Задачі, в яких необхідно зберігати та обробляти наперед не відому кількість елементів, зазвичай потребують динамічного виділення пам’яті.
Динамічне виділення пам’яті у мові С++ здійснюється за допомогою оператора new. Оператор new здійснює пошук неперервної області пам’яті в області пам’яті, що зветься некерована куча. Некерована куча – це стрктура даних за допомогою якої реалізована пам’ять, що може бути виділена динамічно в ході виконання програми, а також це область пам’яті, зарезервована під цю структуру. З іншої сторони куча - це довгий відрізок адрес пам'яті, поділений на блоки різних розмірів, що йдуть підряд. Пам’ять у кучі поділяється на заняту і вільну. Перед початком роботи програми вся пам’ять у кучі позначається як вільна. При виклику оператора динамічного виділення пам’яті у кучі відбувається пошук неперервного сегменту вільної пам’яті заданого розміру. Час такого пошуку є значним і займає більшу частину часу, що необхідна для виконання операції динамічного виділення пам’яті. Якщо такий сегмент було знайдено в кучі, то він помічається як занятий і програмі повертається адреса його початку, інакше – програмі повертається ознака відсутності такого сегменту в пам’яті, найчастіше NULL. Якщо в ході виконання програми значення адреси початку цього сегменту втрачається, то заняту пам’ять звільнити буде неможливо. Якщо ця ситуація проявляється неодноразово, то це може призвести до вичерпання вільної пам’яті в системі. Коли динамічно виділена область пам’яті стає непотрібною, то її потрібно звільнити за допомогою оператора звільнення динамічно виділеної пам’яті. При його виклику область пам’яті на яку вказує вказівник і яка була попередньо динамічно виділена з кучі позначається як вільна і її можна буде в подальшому використовувати заново.
Оператор динамічного виділення пам’яті new може мати дві форми:
Виділення пам’яті під одиничний об’єкт має наступний синтаксис:
тип *вказівник = new тип;
Наприклад, при виконанні оператора
іnt *іp = new іnt;
створюються 2 об'єкти: динамічний безіменний об'єкт розміром 4 байти (значення типу int займає 4 байти) і вказівник на нього з ім'ям іp розміром також 4 байти (у 32-ох бітній системі адреса займає 32 біти), значенням якого є адреса у пам’яті динамічного об'єкта. Можна створити й інший вказівник на той же динамічний об'єкт:
іnt *other = іp;
Якщо вказівникові іp присвоїти інше значення, то можна втратити доступ до динамічного об'єкта:
іnt *іp = new іnt;
іnt і = 0;
іp = &і;
У результаті динамічний об'єкт як і раніше буде існувати, але звернутися до нього буде вже не можна. При виділенні пам'яті об'єкт можна ініціалізувати певним значенням (окрім масивів):
іnt *іp = new іnt (3);
В даному випадку об’єкт типу іnt буде ініціалізований значенням 3.
Виділення пам’яті під масив заданого розміру має наступний синтаксис:
тип *вказівник = new тип[розмір_масиву];
Наприклад, при виконанні оператора
double *mas = new double [50];
виділяється пам’ять під масив з 50 елементів типу double. Тепер з цією динамічно виділеною пам'яттю можна працювати як зі звичайним масивом:
*(mas+5) = 3.27;
mas[6] = mas[5] + sіn(mas[5]);
У випадку успішного завершення операція new повертає вказівник зі значенням, відмінним від нуля. Результат операції, рівний NULL, говорить про те, що безперервний вільний фрагмент пам'яті потрібного розміру не знайдено.
Оператор звільнення динамічної пам'яті delete звільняє для подальшого використання в програмі ділянку пам'яті, яка була раніше виділена оператором new. Синтаксис оператора delete має наступний вигляд:
delete вказівник; // видалення одиничного
динамічного об’єкту
delete[] вказівник; // видалення динамічного
масиву
Наприклад:
delete іp; // Видаляє динамічний об'єкт типу іnt,
що створений як іp = new іnt;
delete[] mas; // видаляє динамічний масив
довжиною 50, що створений як
double *mas = new double[50];
Операції new і delete дозволяють створювати і видаляти багатомірні динамічні масиви, підтримуючи при цьому ілюзію довільної розмірності.
Для створення динамічного двовимірного масиву використовуються наступні елементи:
вказівник на вказівник, який містить адресу початку допоміжного масиву адрес розмір якого рівний висоті двовимірного масиву (кількості рядків);
допоміжний масив адрес, що зберігає адреси одновимірних масивів, які власне міститимуть дані; розмір цих масивів рівний розміру ширини двовимірного масиву (кількості стовпців);
множина масивів, що зберігають дані (реалізують рядки масиву).
Якщо вимірів більше, то використовується більша кількість допоміжних масивів до яких приєднуватимуться інші масиви, завдяки чому власне і утворюватимуться нові виміри. Загалом можна сказати: скільки зірочок при оголошенні базового вказівника на багатовимірний масив, стільки вимірів міститиме цей масив.
Завдання:
Написати програму яка буде використовувати динамічні масиви.
У моїй програмі динамічні масиви використовуються для формування масиву рядків файлу та масиву об’єктів класу WorkerTable.
В файлі WorkerTable.h прописуємо методи за допомогою яких ми формуватимемо масиви рядків та масиви об’єктів.
В файлі WorkerTable.cpp прописуємо реалізації цих методів.
В файлі Sourse.cpp в якому є виконавчий метод main створюємо об’єкт який буде викликати наші методи.
Код Sourse.cpp:
#include "WorkerTable.h"
int main()
{
setlocale(LC_CTYPE, "ukr");
string a, c, d, g;
int b = 1, e = 1;
WorkerTable vasuluna(a, c, d, b, e, g); //Створюємо обєкт класу
int numerator = vasuluna.getNumberLine(); //Обєкт викликає метод що підраховує кількість рядків
string* stringArray = vasuluna.getStrArray(numerator); //Обєкт викликає метод що створює масив строк з рядків файлу
WorkerTable** pointerStringArray = vasuluna.getArray(stringArray, numerator); //Обєкт викликає метод задопомогою якого ми отримуємо масив з обєктів нашого файлу
cout << setw(20) << "Тип роботи" << setw(20) << "Iм'я" << setw(17) << "Прiзвище" << setw(25) << "Кiлькiсть рокiв" << setw(11) << "Стаж" << setw(21) << "Дата народження" << setw(20) << endl;
for (int i = 0; i < numerator; i++) {
cout.width(20);
cout << pointerStringArray[i]->getTypeOfWork();
cout.width(20);
cout << pointerStringArray[i]->getWorkerName();
cout.width(20);
cout << pointerStringArray[i]->getWorkerSurname();
cout.width(17);
cout << pointerStringArray[i]->getYearsNumber();
cout.width(15);
cout << pointerStringArray[i]->getExperience();
cout.width(20);
cout << pointerStringArray[i]->getBirthday();
cout.width(20);
cout << endl;
}
}
Код WorkerTable.cpp:
#include "WorkerTable.h"
string WorkerTable::getTypeOfWork() {
return typeOfWork;
}
string WorkerTable::getWorkerName() {
return workerName;
}
string WorkerTable::getWorkerSurname() {
return workerSurname;
}
int WorkerTable::getYearsNumber() {
return yearsNumber;
}
int WorkerTable::getExperience() {
return experience;
}
string WorkerTable::getBirthday() {
return birthday;
}
int WorkerTable::getNumberLine() {
int numerator = 0;
char *str = new char[1024];
ifstream infile;
infile.open("Dovidnuk2.txt");
// Перевірка чи файл відкривається
if (!infile)
{
cout << "Неможливо вiдкрити файл" << endl;
}
// Підраховуємо кількість строк
while (!infile.eof())
{
infile.getline(str, 1024, '\n');
numerator++;
}
numerator--;
return numerator;
}
string* WorkerTable::getStrArray(int numerator) { //Створюємо метод класу що створює масив строк з рядків файлу та повертає вказівник
string *lineArray = new string[numerator];
ifstream infile;
infile.open("Dovidnuk2.txt");
if (!infile)
{
cout << "Неможливо вiдкрити файл" << endl;
}
for (int i = 0; i < numerator; i++)
{
getline(infile, lineArray[i]);
}
return lineArray;
}
WorkerTable** WorkerTable::getArray(string * lineArray, int numerator) { //Створюємо метод класу що створює масив обєктів класу та повертає вказівник
WorkerTable ** infoArray = new WorkerTable*[numerator];
for (int i = 0; i < numerator; i++)
{
//Парсинг строки
vector<std::string> localArray;
size_t pos = 0, found;
while ((found = lineArray[i].find_first_of(';', pos)) != string::npos) {
localArray.push_back(lineArray[i].substr(pos, found - pos));
pos = found + 1;
}
localArray.push_back(lineArray[i].substr(pos));
infoArray[i] = new WorkerTable(localArray[0], localArray[1], localArray[2], atoi(localArray[3].c_str()), atoi(localArray[4].c_str()), localArray[5]); //
}
return infoArray;
}
Код WorkerTable.h:
#pragma once
#include <string>
#include <iostream>
#include<fstream>
#include<iomanip>
#include <vector>
using namespace std;
class WorkerTable
{
string typeOfWork;
string workerName;
string workerSurname;
int yearsNumber;
int experience;
string birthday;
public:
WorkerTable(string typeOfWork, string workerName, string workerSurname, int yearsNumber, int experience, string birthday) {
this->typeOfWork = typeOfWork;
this->workerName = workerName;
this->workerSurname = workerSurname;
this->yearsNumber = yearsNumber;
this->experience = experience;
this->birthday = birthday;
}
string *getStrArray(int numerator); //Створюємо метод який повертає вказівник на масив строчок
WorkerTable** getArray(string* lineArray, int numerator); //Створюємо масив з класу WorkerTable
//Створюємо методи для встановлення і отримання властивостей класу
int getNumberLine();
string getTypeOfWork();
string getWorkerName();
string getWorkerSurname();
int getYearsNumber();
int getExperience();
string getBirthday();
};
Результат виконання програми:
/
Рис.1 - Ескіз екрана виконаної програми(Вивід інформації про робітників на екран)
Висновок: виконуючи третю лабораторну роботу я отримав знання та навички у використанні динамічних масивів, що стануть мені в пригоді при роботі над курсовою роботою. В подальшому я планую навчитися виконувати редагування цього списку робітників.