Інформація про навчальний заклад

ВУЗ:
Національний університет Львівська політехніка
Інститут:
О
Факультет:
ЗІ
Кафедра:
Кафедра САПР

Інформація про роботу

Рік:
2009
Тип роботи:
Розрахунково - графічна робота
Предмет:
Програмування
Група:
ІТП

Частина тексту файла (без зображень, графіків і формул):

МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ Національний університет “Львівська політехніка” Кафедра САПР РЕАЛІЗАЦІЯ ПРИНЦИПІВ ОБЄКТНО-ОРІЄНТОВАНОГО ПРОЕКТУВАННЯ В МОВІ ПРОГРАМУВАННЯ С++ Розрахунково-графічна робота з курсу “Методи та засоби об’єктно-орієнтованого проектування” Індивідуальне завдання ВАРІАНТ № 1 Використовуючи методологію об’єктно-орієнтованого програмування, скласти програму на мові С++ для вирішення задачі роботи з лінійним однонаправленим списком (формування списку, виведення списку, пошук заданого елемента у списку). Мета роботи. Ознайомлення з основними принципами об’єктно-орієнтованого підходу та їх реалізацією в мові С++. Вступ Коротка характеристика основних принципів об’єктно-орієнтованого підходу. Об'єктно-орієнтованее програмува́ння  (ООП)  — одна з парадигм програмування, яка розглядає програму як множину «об'єктів», що взаємодіють між собою. В ній використано декілька технологій від попередніх парадигм, зокрема успадкування, модульність, поліморфізм та інкапсуляцію. Не зважаючи на те, що ця парадигма з'явилась в 1960-тих роках, вона не мала широкого застосування до 1990-тих. На сьогодні багато із мов програмування (зокрема, Java,  C#, C++, Python, PHP, Ruby та Objective-C) підтримують ООП. Об'єктно-орієнтоване програмування сягає своїм корінням до створення мови програмування Симула в 1960-тих роках, одночасно з посиленням дискусій про кризу програмного забезпечення. Разом із тим, як ускладнювалось апаратне та програмне забезпечення, було дуже важко зберегти якість програм. Об'єктно-орієнтоване програмування частково розв'язує цю проблему шляхом наголошення на модульності програми. На відміну від традиційних поглядів, коли програму розглядали як набір підпрограм, або як перелік інструкцій комп'ютеру, ООП програми можна вважати сукупністю об'єктів. Відповідно до парадигми об'єктно-орієнтованого програмування, кожний об'єкт здатний отримувати повідомлення, обробляти дані, та надсилати повідомлення іншим об'єктам. Кожен об'єкт — своєрідний незалежний автомат з окремим призначенням та відповідальністю. Реалізація принципів об’єктно-орієнтованого підходу в мові програмування С++. Сі++ додає до Сі об'єктно-орієнтовані можливості. Він вводить класи, які забезпечують три найважливіші властивості ООП: інкапсуляцію, успадкування і поліморфізм. Проблеми старого підходу В мові C основним способом організації даних були структури. Структура складається з набору полів, які ніяк не захищені. Якщо елементи структури мають змінну довжину, їх представляють у вигляді вказівників. Виділення і звільнення пам'яті під ці вказівники робляться вручну. Так, наприклад, одновимірний масив змінної довжини в мові C з перевіркою меж може представлятися таким чином: struct Array { double* val; int len; }; void FreeArray (const struct Array*); void AllocArray (const struct Array*, int len); double Elem (const struct Array*, int i); void ChangeElem (const struct Array*, int i, double x); Така реалізація небезпечна і неефективна з багатьох причин: Необхідно викликати FreeArray і AllocArray. Програміст може забути викликати одну з цих функцій, або викликати її дуже рано/запізно, або двічі, або з вказівником на неправильний масив. Все це приводить до помилок, що важко виявити. Функції Elem і ChangeElem повільні. Немає ніякого способу перешкодити програмістам створювати і інші функції для роботи із структурою Array. Ці функції можуть робити з полями len і val будь-що. Немає ніякого способу перешкодити програмістам безпосередньо міняти поля len і val. Присвоєння об'єктів типу struct Array приведе до того, що їх поля val указуватимуть на одну і ту ж область пам'яті. Немає ніякого способу ні заборонити присвоєння, ні змінити таку поведінку. Мова Сі++, використовуючи ООП, усуває всі ці проблеми. Інкапсуляція Основним способом організації інформації в Сі++ є класи. На відміну від типу структура (struct) мови Сі, що складається тільки з полів, клас (class) Сі++ складається з полів і функцій-членів або методів (англ. member functions). Поля бувають публічними (public), захищеними (protected) і приватними (private). У Сі++ тип структура аналогічний типу клас, відмінність в тому, що за умовчанням поля і функції-члени у структури публічні, а у класу — приватні. З публічними полями можна робити зовні класу все, що завгодно. До захищених і приватних полів не можна звертатися ззовні класу, щоб не порушити цілісність даних класу. Спроба такого звернення викличе помилку компіляції. До таких полів можуть звертатися тільки функції-члени класу (а також так звані функції-друзі і функції-члени класів-друзів; про поняття друзів в C++ дивись нижче.) Поза тілом функцій-членів (а також друзів) захищені і власні поля недоступні навіть для читання. Такий захист полів називається інкапсуляциєю. Використовуючи інкапсуляцію, автор класу може захистити свої дані від некоректного використання. Крім того, вона замислювалася для полегшення сумісної розробки класів. Малося на увазі, що зміна способу зберігання даних, якщо вони оголошені як захищені або приватні не вимагає відповідних змін в класах, які використовують змінений клас. Наприклад, якщо в старій версії класу дані зберігалися у вигляді лінійного списку, а в новій версії — у вигляді дерева, ті класи, які були написані до зміни формату зберігання даних, переписувати не буде потрібно, якщо дані були приватними або захищеними (у останньому випадку — якщо використовуючі класи не були класами-нащадками), оскільки жоден з них цих класів не міг би безпосередньо звертатися до даних, а тільки через стандартні функції, які в новій версії мають вже коректно працювати з новим форматом даних. Навіть оператор доступу operator [] може бути визначений як така стандартна функція. Функції-члени, як і поля, можуть бути публічними, захищеними і приватними. Публічні функції може викликати будь-хто, а захищені і власні — тільки функції-члени і друзі. Використовуючи інкапсуляцію, структуру Array з попереднього розділу можна переписати таким чином: class Array { public: void Alloc(int new_len); void Free(); inline double Elem(int i); inline void ChangeElem(int i, double x); protected: int len; double* val; }; void Array::Alloc(int new_len) {if (len>0) Free(); len=new_len; val=new double[new_len];} void Array::Free() {delete [] val; len=0;} inline double Array::Elem(int i) {assert(i>=0 && i<len ); return val[i];} inline void Array::ChangeElem(int i, double x) {assert(i>=0 && i<len); val[i]=x;} І далі Array a; a.Alloc(10); a.ChangeElem(3, 2.78); double b = a.Elem(3); a.Free(); Тут масив а має 4 публічних функції-члена і 2 захищених поля. Описувач inline означає, що замість виклику функції її код підставляється в точку виклику, що вирішує проблему неефективності. Опис функцій в тілі класу В тілі класу можна вказати тільки заголовок функції, а можна описати всю функцію. У другому випадку вона вважається вбудованою (inline), наприклад: class Array { public: void Alloc(int _len) {if (len==0) Free(); val=new double [len=_len];} і так далі. Конструктори і деструктори Проте в приведеному прикладі не вирішена важлива проблема: функції Alloc і Free як і раніше треба викликати вручну. Інша проблема даного прикладу — небезпека оператора присвоєння. Для вирішення цих проблем в мову були введені конструктори і деструктори. Конструктор викликається кожного разу, коли створюється об'єкт даного типу; деструктор — при знищенні. При перетвореннях типів, присвоєнні, передачі параметра теж викликаються конструктори і при необхідності деструктори. З конструкторами і деструкторами клас виглядає так: class Array { public: Array() : len(0), val(NULL) {} Array(int _len) : len(_len) {val = new double[_len];} Array(const Array& a); ~Array() { Free(); } inline double Elem(int i); inline void ChangeElem(int i, double x); protected: void Alloc(int _len); void Free(); int len; double* val; }; Array::Array(const Array& a) : len(a.len) { val = new double[len]; for (int i=0; i<len; i++) val[i] = a.val[i]; } Тут Array::Array — конструктор, а Array::~Array — деструктор. Конструктор копіювання (англ. copy constructor) Array::Array(const Array&) викликається при присвоєнні. Тепер об'єкт класу Array не можна зіпсувати: як би ми його не створювали, що б ми не робили, його значення буде коректним, тому що конструктор викликається автоматично. Всі небезпечні операції з вказівниками заховані в захищені функції. Array a(5); // викликається Array::Array(int) Array b; // викликається Array::Array() Array c(a); // викликається Array::Array(const Array&) Array d=a; // те саме b=c; // відбувається виклик оператора = // якщо він не визначений (як в даному випадку), то викликається оператор присвоєння за умовчанням, який // здійснює побітове копіювання для базових типів і виклик оператора присвоєння для користувача // як правило, конструктор копій і оператор присвоєння перевизначаються попарно Оператор new теж викликає конструктори, а delete — деструктори. За умовчанням, кожен клас має конструктор без параметрів і деструктор. Конструктор без параметрів за умовчанням викликає конструктори всіх елементів, а деструктор — їх деструктори. Інші конструктори за умовчанням не визначені. Клас може мати скільки завгодно конструкторів (з різними наборами параметрів), але тільки один деструктор (без параметрів). Інші можливості функцій-членів Функції-члени можуть бути і операціями: class Array { … inline double &operator[] (int n); І далі Array a(10); … double b = a[5]; Функції-члени (і лише вони) можуть мати описувач const class Array { … inline double operator[] (int n) const; Такі функції не мають права змінювати поля класу (окрім полів, визначених як mutable). Якщо вони намагаються це зробити, компілятор повинен видати повідомлення про помилку. Успадкування Для створення класів з доданою функціональністю вводять спадкування. Клас-нащадок має поля і функції-члени базового класу, але не має права звертатися до приватних (private) полів і функцій базового класу. У цьому і полягає різниця між приватними і захищеними членами. Клас-нащадок може додавати свої поля і функції або перевизначати функції базового класу. За умовчанням, конструктор нащадка без параметрів викликає конструктор базового класу, а потім конструктори доданих елементів. Деструктор працює в зворотному порядку. Інші конструктори доводиться визначати кожного разу наново. На щастя, це можна зробити викликом конструктора базового класу. class ArrayWithAdd : public Array { ArrayWithAdd(int n) : Array(n) {} ArrayWithAdd() : Array() {} ArrayWithAdd(const Array& a) : Array(a) {} void Add(const Array& a); }; Нащадок — це більш ніж базовий клас, тому він може використовуватися скрізь, де використовується базовий клас, але не навпаки. Успадкування буває публічним, захищеним і власним. При публічному спадкуванні, публічні і захищені члени базового класу зберігають свій статус, а до приватних не можуть звертатися навіть функції-члени нащадка. Захищене спадкування відрізняється тим, що при нім публічні члени базового класу є захищеними членами нащадка. При приватному успадкуванні, до жодного члена базового класу навіть функції-члени нащадка права звертатися не мають. Як правило, публічне спадкування зустрічається значно частіше за інші. Клас може бути нащадком декількох класів. Це називається множинним спадкуванням. Такий клас володіє полями і функціями-членами всіх його предків. Наприклад, клас FlyingCat може бути нащадком класів Cat і FlyingAnimal. class Cat { ... void Purr(); ... }; class FlyingAnimal { ... void Fly(); ... }; class FlyingCat : public Cat, public FlyingAnimal { ... PurrAndFly() {Purr(); Fly();} ... }; Поліморфізм Поліморфізмом в програмуванні називається перевизначення нащадком функцій-членів базового класу, наприклад class Figure { ... void Draw() const; ... }; class Square : public Figure { ... void Draw() const; ... }; class Circle : public Figure { ... void Draw() const; ... }; В даному прикладі, яка з функцій буде викликана — Circle::Draw(), Square::Draw() або Figure::Draw(), визначається під час компіляції. Наприклад, якщо написати Figure* x = new Circle(0,0,5); x->Draw(); то буде викликане Figure::Draw(), оскільки x — об'єкт класу Figure. Такий поліморфізм називається статичним. Але в C++ є і динамічний поліморфізм, коли функція, що викликається, визначається під час виконання. Для цього функції-члени повинні бути віртуальними. class Figure { ... virtual void Draw() const; ... }; class Square : public Figure { ... virtual void Draw() const; ... }; class Circle : public Figure { ... virtual void Draw() const; ... }; Figure* figures[10]; figures[0] = new Square(1, 2, 10); figures[1] = new Circle(3, 5, 8); … for (int i = 0; i < 10; i++) figures[i]->Draw(); В цьому випадку для кожного елементу буде викликана Square::Draw() або Circle::Draw() залежно від виду фігури. Чисто віртуальною функцією називається функція-член, яка не визначена в базовому класі, а тільки в нащадках: class Figure { ... virtual void Draw() const = 0; ); Ось це = 0 і означає, що функція чисто віртуальна. Абстрактним класом називається такий, у якого є хоч би одна чисто віртуальна функція-член. Об'єкти таких класів створювати заборонено. Абстрактні класи використовуються як інтерфейси. Друзі Функції-друзі — це функції, що не є функціями-членами, проте мають доступ до захищених і власних полів і функцій-членів класу. Вони повинні бути описані в тілі класу як friend. Наприклад: class Matrix { ... friend Matrix Multiply (Matrix m1, Matrix m2); ... }; Matrix Multiply (Matrix m1, Matrix m2) { ... } Тут функція Multiply може звертатися до будь-яких полів і функцій-членів класу Matrix. Існують також класи-друзі. Якщо клас A — друг класу B, то всі його функції-члени можуть звертатися до будь-яких полів і функцій членів класу B. Наприклад: class Matrix { ... friend class Vector; ... }; Проте в С++ не діє правило «друг мого друга — мій друг». За стандартом C++03 вкладений клас не має прав доступу до закритих членів охоплюючого класу і не може бути оголошений його другом (це виходить з визначення терміну друг як нечлена класу). Проте, багато розповсюджених компіляторів порушують обидва ці правила (може, зважаючи на сукупну дивність цих правил). Опис алгоритму розв’язку прикладної задачі. Для реалізації списку потрібно 2 класи: 1) контейнер для даних (class someData); 2) власне список (class List); Контейнер для даних складається з власне даних для яких реалізується список (в даному випадку одне поле типу INT), індекса і вказівника на наступний елемент типу «контейнер»: int data; unsigned int index; someData* next; Також контейнер має один метод showItem(), який виводить форматовані дані про елемент (”Item[<index>] = <value>\n”). Власне список складається з вказівника на перший елемент типу «контейнер» і кількості всіх елементів у списку: someData* first; unsigned int count; Клас List має такі методи: 1) додавання нового елемента у список; Створюється новий екземпляр класу someData. Полю next цього класу присвоюється адреса first класу List. Полю first класу List присвоюється адреса someData. void addItem(int data = 0); 2) виведення всіх елементів списку на консоль. void showItems(); 3) пошук елемента за значенням і виведення даних на консоль. void findItem(int tofind); 4) перегружений оператор “[ ]”, який дозволяє звертатися до елементів списку по індексу. someData* operator[](int i) Програмна реалізація. //ВАРІАНТ № 1 //Використовуючи методологію об’єктно-орієнтованого програмування, //скласти програму на мові С++ для вирішення задачі //роботи з лінійним однонаправленим списком (формування списку, виведення //списку, пошук заданого елемента у списку). #include <conio.h> #include <stdio.h> #include <iostream> #include <string.h> class someData { public: int data; //age data unsigned int index; //index of an item, to be able get item by index someData* next; //Link to a next object of someData class //Constructor with changeble count of parameters someData(int id, int a = 0){ data = a; index = id; } //Destructors ~someData(){} //prints item info [Item[<index>] = <value>] void showItem() { printf("Item[%d] = %d\n", index, data); } }; class List { private: someData* first; // Link to a first created object unsigned int count; // Total count of items public: List(){ count = 0; first = NULL; } //operators //operator "[]", that allows to get an object by index //parameters: i - index //returns: a pointer to a someData object on index; NULL - index is bigger then global count of items, or <= 0 someData* operator[](int i) { someData* togo = first; if(i>count || i<=0) return NULL; if(togo->index == i) return togo; do { togo = togo->next; if(togo->index == i) return togo; } while (togo->next!=NULL); } //methods //Constructor with changeble count of parameters void addItem(int data = 0) { someData* tmp = new someData(++count, data); tmp->next = first; first = tmp; } //prints all the items of the List void showItems() { someData* togo = first; togo->showItem(); do { togo = togo->next; togo->showItem(); } while(togo->next!=NULL); } //finds and prints all of the items with values you want; //parameters: a value to find void findItem(int tofind) { unsigned int count = 0; someData* togo = first; printf("You'r trying to find item = %d\n",tofind); if(togo->data == tofind) { togo->showItem(); ++count; } do { togo = togo->next; if(togo->data == tofind) { togo->showItem(); ++count; } } while(togo->next!=NULL); printf(count == 1? "Found 1 item.\n":"Found %d items.\n",count); } //returns: the number of items unsigned int getCount() { return count; } }; void main(void) { List* l = new List(); //adding new items to List l->addItem(5); l->addItem(3); l->addItem(3); l->addItem(4); l->addItem(3); //printing all of the items by built in List method l->showItems(); //finding any item you want by value l->findItem(3); //printing all of the items by indexes someData* s; for(int i = 1; i<=l->getCount();i++) { s = (*l)[i]; s->showItem(); } _getch(); } Результат виконання: Item[5] = 3 Item[4] = 4 Item[3] = 3 Item[2] = 3 Item[1] = 5 You'r trying to find item = 3 Item[5] = 3 Item[3] = 3 Item[2] = 3 Found 3 items. Item[1] = 5 Item[2] = 3 Item[3] = 3 Item[4] = 4 Item[5] = 3 Висновок. Виконуючи дане завдання я навчився працювати з вказівниками та створювати динамічні обєкти. Використана література: Гради Буч "Разработка объектно-ориентированного программного обеспечения".
Антиботан аватар за замовчуванням

20.07.2020 13:07-

Коментарі

Ви не можете залишити коментар. Для цього, будь ласка, увійдіть або зареєструйтесь.

Ділись своїми роботами та отримуй миттєві бонуси!

Маєш корисні навчальні матеріали, які припадають пилом на твоєму комп'ютері? Розрахункові, лабораторні, практичні чи контрольні роботи — завантажуй їх прямо зараз і одразу отримуй бали на свій рахунок! Заархівуй всі файли в один .zip (до 100 МБ) або завантажуй кожен файл окремо. Внесок у спільноту – це легкий спосіб допомогти іншим та отримати додаткові можливості на сайті. Твої старі роботи можуть приносити тобі нові нагороди!
Нічого не вибрано
0%

Оголошення від адміністратора

Антиботан аватар за замовчуванням

Подякувати Студентському архіву довільною сумою

Admin

26.02.2023 12:38

Дякуємо, що користуєтесь нашим архівом!