Розробка власного класу STRING

Поняття об’єктно-орієнтовного програмування, поява відносин класів. Структури як складені типи даних, побудовані з використанням інших типів. Область дії класу і доступ до його елементів. Конструктор та деструктор класу. Розробка базового класу STRING.

Рубрика Программирование, компьютеры и кибернетика
Вид курсовая работа
Язык украинский
Дата добавления 19.08.2010
Размер файла 280,9 K

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.

19

Міністерство освіти і науки України

Дніпропетровський національний університет

Факультет фізики, електроніки та комп'ютерних систем

Курсова робота

з дисципліни

об'єктно-орієнтовне програмування

на тему: "Розробка власного класу String"

Виконав:

ст. гр. РС-05-1

Тимощенко П.А.

Перевірив:

доц. Вовк С.М.

Дніпропетровськ 2007

Содержание

  • Завдання
    • 1. Теоретична частина
    • 1.1 Введення в об'єктно-орієнтовну технологію
    • 1.2 Визначення структур
    • 1.3 Доступ до елементів структури
    • 1.4 Використання визначеного користувачем типу Time за допомогою Struct
    • 1.5 Використання абстрактного типу даних Time за допомогою класу
    • 1.6 Область дії клас і доступ до елементів класу
    • 1.7 Конструктор класу
    • 1.8 Конструктор копіювання
    • 1.9 Деструктор класу
    • 1.10 Явний виклик деструктора
    • 1.11 Небезпека збільшення розміру програми
    • 1.12 Константні об'єкти й функції-елементи
    • 1.13 Друзі
    • 1.14 Ядро ООП: Успадкування та поліморфізм
    • 1.4.1 Похідні класи
    • 1.14.2 Функції-члени
    • 1.14.3 Конструктори й деструктори
    • 1.14.4 Ієрархія класів
    • 1.14.5 Поля типу
    • 1.14.6 Віртуальні функції
    • 1.14.7 Абстрактні класи
    • 1.14.8 Множинне входження базового класу
    • 1.14.9 Вирішення неоднозначності
    • 1.14.10 Віртуальні базові класи
    • 1.14.11 Контроль доступу
    • 1.14.12 Захищені члени
    • 1.14.13 Доступ до базових класів
    • 1.14.14 Вільна пам'ять
    • 1.14.15 Віртуальні конструктори
    • 1.15 Перевантаження операцій
    • 1.15.1 Операторні функції
    • 1.15.2 Бінарні й унарні операції
    • 1.15.3 Операторні функції й типи користувача
    • 1.15.4 Конструктори
    • 1.15.5 Присвоювання й ініціалізація
    • 1.15.6 Інкремент і декремент
    • 1.15.7 Перевантаження операцій помістити в потік і взяти з потоку
    • 2. Розробка власного класу clsString
    • 2.1 Загальний алгоритм вирішення
    • 2.2 Детальний анализ
    • 2.3 Тестування
    • Висновки
    • Література
    • Додатки

Завдання

Розробити клас classString, на основі якого можна створювати об'єкти типу "рядок символів". Цей клас повинен надавати можливість створення програм, в яких реалізуються обробка рядків символів. В класі повинні бути визначені методи присвоєння рядків, додавання рядків, вставки рядка в рядок з заданого місця та вилучення певної кількості символів з рядка, звертання до окремого елементу рядка, операції відношень для порівняння рядків (більше, менше, рівно, нерівно), операції вставки рядка в потік введення/виведення та його вилучення з потоку, метод визначення довжини рядка, тощо. Розробку виконувати в середовищі Borland C++ Builder або MS Visual Studio C++.

1. Теоретична частина

1.1 Введення в об'єктно-орієнтовну технологію

Подивіться навколо себе на реальній світ. Куди б ви не подивились завжди знаходяться об'єкти! Люди, тварини, рослини, автомобілі, літаки, комп'ютери і тощо. Людина кумекає в термінах об'єктів. Мі володіємо чудовою можливістю абстрагувати, що дозволяє нам бачити картинки на екрані (людей, дерева, літаки) саме у вигляді об'єктів, а не у вигляді окремих кольорових точок.

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

Людство пізнає об'єкти шляхом вивчення їх атрибутів. Різні об'єкти можуть мати багато однакових атрибутів та представляти схожу поведінку.

Об'єктно-орієнтовне програмування (ООП) моделює об'єкти реального світу за допомогою програмних аналогів. Це приводить до появи відносин класів, коли об'єкти певного класу - такого, як клас засобів пересування - мають однакові характеристики. Це висуває відносини спадкування й навіть відносини множинного спадкування, коли знову створювані класи здобувають наслідувані характеристики існуючих класів, а також містять свої власні унікальні характеристики. Об'єкти класу автомобілів з відкидним верхом виразно мають характеристики класу автомобілів, але дах у них відкидається й закривається.

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

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

У С та інших мовах процедурного програмування програмування має тенденцію бути орієнтованим на дії, тоді як в C++ програмування прагне бути орієнтованим на об'єкти. У С одиницею програмування є функція. В C++ одиницею програмування є клас, на основі якого в кінцевому результаті створюються екземпляри об'єктів.

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

Програмісти на C++ зосереджені на створенні своїх власних обумовлених користувачем типів, названих класами. Кожний клас містять дані й набір функцій, які маніпулюють цими даними. Компоненти дані класу називаються даними-елементами (елементами даних). Компоненти функції класу називаються функціями-елементами. Точно так само, як екземпляр вбудованого типу, такого як int, називається змінної, екземпляр певного користувачем типу (тобто класу) називається об'єктом. Програміст використає вбудовані типи як блоки для конструювання певних користувачем типів. В C++ увага фокусується скоріше на об'єктах, чим на функціях. Імена іменники в описі системи допомагають програмістові на C++ при створенні системи визначити набір класів, з яких будуть створені об'єкти, які, працюючи спільно, і забезпечують функціонування системи.

Класи для об'єктів є тим же самим, що і проекти для домів. Можна збудувати багато домів згідно одного проекту, і можна реалізувати багато об'єктів з одного класу. Наприклад, в об'єктно-орієнтовному проектуванні банку клас BankTeller, повинен співвідноситися з класом BankAccount. Ці співвідношення називають асоціативними.

Класи в С++ є природнім продовженням структури struck в мові С. Тому, перш ніж, розглядати специфіку розробки класів на С++, мі розглянемо та побудуємо визначений користувачем тип, оснований на структурі. Недоліки, які ми побачимо при цьому, допоможуть пояснити запис класу.

1.2 Визначення структур

Структури - це складені типи даних, побудовані з використанням інших типів. Розглянемо наступне визначення структури:

struct Time

{

int hour; // 0-23

int minute; // 0-59

int second; // 0-59

}

Ключове слово struct починає визначення структури. Ідентифікатор Time - тег (позначення, ім'я-етикетка) структури. Тег структури використається при об'явленні змінних структур даного типу. У цьому прикладі ім'я нового типу - Time. Імена, об'явленні у фігурних дужках опису структури - це елементи структури. Елементи однієї й тієї ж структури повинні мати унікальні імена, але дві різні структури можуть містити не конфліктуючі елементи з однаковими іменами. Кожне визначення структури повинне закінчуватися крапкою з комою. Наведене пояснення, як ми незабаром побачимо, вірно й для класів.

Визначення Time містить три елементи типу int - hour, minute і second (годинники, хвилини й секунди). Елементи структури можуть бути будь-якого типу й одна структура може містити елементи багатьох різних типів. Структура не може, однак, містити екземпляри самої себе. Наприклад, елемент типу Time не може бути оголошений у визначенні структури Time. Однак, може бути включений вказівник на іншу структуру Time. Структура, що містить елемент, котрий є вказівником на такий же структурний тип, називається структурою із самоадресацією. Структури із самоадресацією корисні для формування зв'язних структур даних.

Попереднє визначення структури даних не резервує ніякого простору в пам'яті; визначення тільки створює новий тип даних, що використається для об'явлення змінних. Змінні структури об'явленні так само, як змінні інших типів. Об'явлення

Time timeObject, timeArray [10], *timePtr;

повідомляє timeObject змінна типу Time, timeArray - масив з 10 елементів типу Time, a timePtr - вказівник на об'єкт типу Time.

1.3 Доступ до елементів структури

Для доступу до елементів структури (або класу) використовуються операції доступу до елементів - операція крапка () і операція стрілка (->).

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

Наприклад, щоб надрукувати елемент hour структури timeObject використається оператор

cout << timeObject. hour;

Операція стрільця, що складається зі знака мінус (-) і знака більше (>), записаних без пропусків, забезпечує доступ до елемента структури (або класу) через вказівник на об'єкт. Припустимо, що вказівник timePtr був уже об'явлений як посилання на об'єкт типу Time і що адреса структури timeObiect була вже присвоєна timePtr. Тоді, щоб надрукувати елемент hour структури timeObject з вказівником timePtr, можна використати оператор

cout << timePtr->hour;

Вираз timePtr->hour еквівалентний (*timePtr). hour, що розіменовує вказівник і робить доступним елемент hour через операцію крапка. Дужки потрібні тому, що операція крапка має більш високий пріоритет, ніж операція розіменування вказівника (*). Операції стрілка й крапка поряд із круглими й квадратними дужками мають другий найвищий пріоритет (після операції дозволу області дії) і асоціативності зліва направо.

1.4 Використання визначеного користувачем типу Time за допомогою Struct

Програма на мал.1 створює визначений користувачем тип структури Time із трьома цілими елементами: hour, minute і second. Програма визначає єдину структуру типу Time, названу dinnerTime, і використовує операцію крапка для присвоєння елементам структури початкових значень 18 для hour, 30 для minute і 0 для second. Потім програма друкує час у військовому (24-годинному) і стандартному (12-годинному) форматах. Помітимо, що функції друку приймають посилання на постійні структури типу Time. Це є причиною того, що структури Time передаються друкуючим функціям по посиланню - цим виключаються накладні витрати на копіювання, пов'язані з передачею структур функціям за значенням, а використання const запобігає зміні структури типу Time функціями друку. Далі ми обговоримо об'єкти const і функції-елементи const.

Порада з підвищення ефективності: Щоб уникнути накладних витрат, пов'язаних із передачею по значенню й одержати користь захисту початкових даних від зміни, передавайте аргументи великого розміру як посилання const.

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

// Створення структури, завдання й друк її елементів.

#include <iostream. h>

struct Time { // визначення структури

int hour; // 0-23

int minute; // 0-59

int second; // 0-59 };

void printMilitary (const Time &); // прототип void printStandard (const Time &); // прототип

main ()

{

Time dinnerTime; // змінна нового типу Time

// завдання елементам правильні значення dinnerTime. hour = 18; dinnerTime. minute = 30; dinnerTime. second = 0;

cout " "Обід відбудеться в ";

printMilitary (dinnerTime);

cout " " за військовим часом," " endl

<< "що відповідає "; printStandard{dinnerTime); cout << " за стандартним часом." << endl;

// завдання елементам неправильних значень

dinnerTime. hour = 29;

dinnerTime. minute = 73; dinnerTime. second = 103;

cout " endl << "Час із неправильними значеннями: "; printMilitary (dinnerTime); cout << endl; return 0;

// Друк часу у військовому форматі void printMilitary (const Time &t)

{

cout " (t. hour < 10?"0": "")" t. hour

"": "" (t. minute < 10?"0": "")" t. minute

"": "" (t. second < 10?"0": "")" t. second;

}

// друк часу в стандартному форматі

void printStandard (const Time &t)

{

cout " ( (t. hour == 0 || t. hour == 12)? 12: t. hour%12)" ": "" (t. minute < 10?"0": "")" t. minute " ": "" (t. second < 10?"0": "")" t. second " (t. hour < 12?" AM": " PM");

}

Обід відбудеться в 18: 30: 00 за військовим часом,

що відповідає 6: 30: 00 РМ за стандартним часом.

Час із неправильними значеннями: 29: 73: 103

Мал.1. Створення структури, завдання й друк її елементів

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

У наступному розділі ми знову використаємо нашу структуру Time, але вже як клас, і продемонструємо деякі переваги створення таких, так званих абстрактних типів даних, як класи. Ми побачимо, що класи й структури в C++ можна використовувати майже однаково. Різниця між ними складається в доступності за замовчуванням елементів кожного із цих типів. Це буде більш детально пояснено пізніше.

1.5 Використання абстрактного типу даних Time за допомогою класу

Класи надають програмістові можливість моделювати об'єкти, які мають атрибути (представлені як дані-елементи) і варіанти поведінки або операції (представлені як функції-елементи). Типи, що містять дані-елементи й функції-елементи, звичайно визначаються в C++ за допомогою ключового слова class.

Функції-елементи іноді в інших об'єктно-орієнтовних мовах називають методами, вони викликаються у відповідь на повідомлення, що посилаються об'єкту. Повідомлення відповідає виклику функції-елемента.

Коли клас визначений, ім'я класу може бути використане для об'явлення об'єкта цього класу. Мал.1 містить просте визначення класу Time.

Визначення нашого класу Time починається із ключового слова class. Тіло визначення класу береться у фігурні дужки ({ }). Визначення класу закінчується крапкою з комою. Визначення нашого класу Time, як і нашої структури Time, містить три цілих елементи hour, minute і second.

сlass Time {

public:

Time ();

void setTime (int, int, int);

void printMilitary ();

void printStandatd (); private:

int hour; // 0-23

int minute; // 0 - 59

int second; // 0-59

};

Мал.1 Просте визначення класу

Інші частини визначення класу - нові. Мітки public: (відкрита) і private: закрита) називаються специфікаторами доступу до елементів. Будь-які дані-елементи й функції-елементи, об'явлені після специфікатора доступу до елементів public: (і до наступного специфікатора доступу до елементів), доступні при будь-якому звертанні програми до об'єкта класу Time. Будь-які дані-елементи й функції-елементи, об'явлені після специфікатора доступу до елементів private: (і до наступного специфікатора доступу до елементів), доступні тільки функціям-елементам цього класу. Специфікатори доступу до елементів завжди закінчуються двокрапкою (:) і можуть з'являтися у визначенні класу багато разів і в будь-якому порядку. Надалі в тексті нашої роботи ми будемо використовувати записи специфікаторів доступу до елементів у вигляді public і private (без двокрапки).

Гарний стиль програмування: Використовуйте при визначенні класу кожний специфікатор доступу до елементів тільки один раз, що зробить програму більш ясною й простій для читання. Розміщайте першими елементи public, що є загальнодоступними.

Визначення класу в нашій програмі містить після специфікатора доступу до елементів public прототипи наступних чотирьох функцій-елементів: Time, setTime, printMilitary і printStandard. Це - відкриті функції-елементи або відкритий інтерфейс послуг класу. Ці функції будуть використовуватися клієнтами класу (тобто частинами програми, що грають роль користувачів) для маніпуляцій з даними цього класу.

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

Після специфікатора доступу до елементів private слідують три цілих елементи. Це говорить про те, що ці дані-елементи класу є доступними тільки функціям-елементам класу й, як ми побачимо далі, "друзям" класу. Таким чином, дані-елементи можуть бути доступні тільки чотирьом функціям, прототипи яких включені у визначення цього класу (або друзів цього класу). Звичайно дані-елементи перераховуються в частині private, а функції-елементи - у частині public. Як ми побачимо далі, можна мати функції-елементи private і дані public; останнє не типовим й вважається в програмуванні поганим тоном.

Коли клас визначений, його можна використати як тип в оголошеннях, наприклад, у такий спосіб:

Time sunset, // об'єкт типу Time

arrayOfTimes [5], // масив об'єктів типу Time

*pointerToTime, // вказівник на об'єкт типу Time

&dinnerTime = sunset; // посилання на об'єкт типу Time

Ім'я класу стає новим специфікатором типу. Може існувати безліч об'єктів класу як і безліч змінних типу, наприклад, такого, як int. Програміст по мірі необхідності може створювати нові типи класів. Це одна з багатьох причин, з яких C++ є розширюваною мовою.

Програма на мал.2 використовує клас Time. Ця програма створює єдиний об'єкт класу Time, названий t. Коли об'єкт створюється, автоматично викликається конструктор Time, що явно привласнює нульові початкові значення всім даним-елементам закритої частини private. Потім друкується час у військовому й стандартному форматах, щоб підтвердити, що елементи одержали правильні початкові значення. Після цього за допомогою функцій-елементів setTime встановлюється час і воно знову друкується в обох форматах. Потім функція-елемент setTime намагається дати даним-елементам неправильні значення й час знову друкується в обох форматах.

Знову відзначимо, що дані-елементи hour, minute і second об'явлені специфікатором доступу до елементів private. Ці закриті дані-елементи класу звичайно недоступні поза класом. Глибокий зміст такого підходу полягає в тому, що реальне становище даних усередині класу не стосується клієнтів класу. Наприклад, було б цілком можливо змінити внутрішню структуру даних і представляти, наприклад, час усередині класу як число секунд після опівночі. Клієнти могли б використати ті ж самі відкриті функції-елементи й одержувати ті ж самі результати, навіть не усвідомлюючи про зроблені зміни. У цьому сенсі, говорять, що реалізація класу схована від клієнтів. Таке приховання інформації сприяє модифікаційності програм і спрощує сприйняття класу клієнтами.

// FIG 3. CPP // Клас Time.

#include <iostream. h>

// Визначення абстрактного типу даних (АТД) Time

class Time{

public:

Time{); // конструктор

void setTime (int, int, int); // установка годин, хвилин

// та секунд

void printMilitary (); // часу у військовому форматі

void printStandard (); // друк часу

// у стандартному форматі

private:

int hour; // 0-23

int minute; // 0-59

int second; // 0-59

// Конструктор Time привласнює нульові початкові значення // кожному елементу даних. Забезпечує погоджене

// початковий стан всіх об'єктів

Time Time:: Time () { hour = minute = second =0; }

// Завдання нового значення Time у вигляді воєнного часу. // Перевірка правильності значень даних.

// Обнуління неправельних значень,

void Time:: setTime (int h, int m, int s) {

hour = (h>=0&&h<24)? h: 0;

minute = (m >= 0 && m < 60)? m: 0;

second ~ (s > - 0 && s < 60)? s: Q-; }

// Друк часу у військовому форматі

void Time:: printMilitary ()

{

cout " {hour < 10?"0": "")" hour" ": "

" (minute < 10?"0": "")" minute " ": "

" (second < 10?"0": "")" second; }

// Друк часу в стандартному форматі void Time:: printStandard ()

{

cout " ( (hour == 0 || hour == 12)? 12: hour% 12)

"": " " (minute < 10?"0": "")" minute

"": " " (second < 10?"0": "")" second

" (hour < 12?" AM": " PM");

}

// Формування перевірки простого класу Time

main ()

{

Time t; // визначення екземпляра об'єкта t класу Time

cout " "Початкове значення воєнного часу дорівнює "; t. printMilitary (); cout << endl

<< "Початкове значення стандартного часу дорівнює "; t. printStandard ();

t. setTime (13, 27,6):

cout " endl " endl << "Воєнний час після setTime дорівнює "; t. printMilitary ();

cout << endl << "Стандартний час після setTime дорівнює"; t. printStandard ();

t. setTime (99, 99, 99); // спроба встановити неправильні значення cout << endl << endl

<< "Після спроби неправильної установки: "

<< endl " "Воєнний час: "; t. printMilitary ();

cout << endl " "Стандартний час: "; t. printStandard (); cout << endl; return 0; }

Мал.2. Використання абстрактного типу даних Time як класу

Початкове значення воєнного часу дорівнює 00: 00: 00 Початкове значення стандартного часу дорівнює 12: 00: 00 AM

Воєнний час після setTime дорівнює 13: 27: 06

Після спроби неправильної установки: Воєнний час: 00; 00: 00 Стандартний час: 12: 00: 00 AM

У нашій програмі конструктор Time просто встановлює початкові значення, рівні 0, даним-елементам, (тобто задає воєнний час, еквівалентний 12AM). Це гарантує, що об'єкт при його створенні перебуває у відомому стані. Неправильні значення не можуть зберігатися в даних-елементах об'єкта типу Time, оскільки конструктор автоматично викликається при створенні об'єкта типу Time, а всі наступні спроби змінити дані-елементи ретельно розглядаються функцією setTime.

Відзначимо, що дані-елементи класу не можуть одержувати початкові значення в тілі класу, де вони оголошуються. Ці дані-елементи повинні одержувати початкові значення за допомогою конструктора класу або їм можна присвоїти значення через функції.

Функція з тим же ім'ям, що й клас, але з символом-тильда (~) перед нею, називається деструктором цього класу (наш приклад не включає деструктор). Деструктор робить "завершальні службові дії над кожним об'єктом класу перед тим, як пам'ять, відведена під цей об'єкт, буде повторно використана системою.

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

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

Відзначимо використання бінарної операції дозволу області дії (::) у кожному визначенні функції-елемента, що випливає за визначенням класу на мал.3. Після того, як клас визначений і його функції-елементи Об'явлені, ці функції-елементи повинні бути описані. Кожна функція-елемент може бути описана прямо в тілі класу (замість включення прототипу функції класу) або після тіла класу. Коли функція-елемент описується після відповідного визначення класу, ім'я функції випереджається ім'ям класу та бінарною операцією дозволу області дії (::). Оскільки різні класи можуть мати елементи з однаковими іменами, операція дозволу області дії "прив'язує" ім'я елемента до імені класу, щоб однозначно ідентифікувати функції-елементи даного класу.

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

Якщо функція-елемент описана у визначенні класу, вона автоматично вбудовується inline. Функція-елемент, описана поза визначенням класу, може бути inline за допомогою явного використання ключового слова inline. Нагадаємо, що компілятор резервує за собою право не вбудовувати ніяких функцій.

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

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

Часто класи не створюються "на порожнім місці". Звичайно вони є похідними від інших класів, що забезпечують нові класи необхідними їм операціями. Або класи можуть включати об'єкти інших класів як елементи. Таке повторне використання програмного забезпечення значно збільшує продуктивність програміста. Створення нових класів на основі вже існуючих класів називається успадкуванням. Включення класів як елементів інших класів називається композицією.

1.6 Область дії клас і доступ до елементів класу

Дані-елементи класу (змінні, об'явлені у визначенні класу) і функції-елементи (функції, об'явлені у визначенні класу) мають областю дії клас. Функції, що не є елементами класу, мають областю дії файл.

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

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

Але, не можна перевантажити функцію-елемент класу за допомогою функції не з області дії цього класу.

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

Операції, для доступу до елементів класу, аналогічні операціям, для доступу до елементів структури. Операція вибору елемента крапка () комбінується для доступу до елементів об'єкта з ім'ям об'єкта або з посиланням на об'єкт. Операція вибору елемента стрілка (->) комбінується для доступу до елементів об'єкта з вказівником на об'єкт.

Програма на мал.3 використає простий клас, названий Count, з відкритим елементом даних х типу int і відкритої функцією-елементом print, щоб проілюструвати доступ до елементів класу за допомогою операції вибору елемента. Програма створює три екземпляри змінних типу Count - counter, counterRef (посилання на об'єкт типу Count) і counterPtr (покажчик на об'єкт типу Count). Змінна counterRef об'явлена, щоб посилатися на counter, змінна counterPtr об'явлена, щоб указувати на counter. Важливо відзначити, що тут елемент даних х зроблений відкритим просто для того, щоб продемонструвати способи доступу до відкритих елементів. Як ми вже встановили, дані звичайно робляться закритими (private).

// FIG6_4. CPP

// Демонстрація операцій доступу до елементів класу. і - >

#include <iostream. h>

// Простий клас Count class Count { public:

int x;

void print () { cout << x " endl; } };

main ()

{

Count counter, // створюється об'єкт counter

*counterPtr = &counter, // покажчик на counter &counterRef = counter; // посиланя на counter

cout " "Присвоювання х значення 7 і друк по імені об'єкта: ";

counter. х =7; // присвоювання 7 елементу даних х

counter. print (); // виклик функції-елемента для друку

cout << "Присвоювання х значення 8 і друк по посиланню: ";

counterRef. x = 8; // присвоювання 8 елементу даних х

counterRef. print (); // виклик функції-елемента для друку

cout << "Присвоювання х значення 10 і друк по покажчику: "; counterPtr->x = 10; // присвоювання 10 елементу даних х counterPtr->print (); // виклик функції-елемента для друку

return 0;

}

Мал.3. Доступ до даних-елементів об'єкта й функціям-елементам за допомогою імені об'єкта, посилання й вказівника на об'єкт

Присвоювання х значення 7 і друк по імені об'єкта: 7

Присвоювання х значення 8 і друк по посиланню: 8

Присвоювання х значення 10 і друк по покажчику: 10

1.7 Конструктор класу

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

class Account {

public:

// конструктор за замовчуванням...

Account ();

// ...

private:

char *_name;

unsigned int _acct_nmbr;

double _balance;

};

Єдине синтаксичне обмеження, що накладає на конструктор, полягає в тому, що він не повинен мати тип значення, що повертає, навіть void.

Кількість конструкторів в одного класу може бути будь-яким, аби тільки всі вони мали різні списки формальних параметрів.

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

Account (const char *name, double open_balance);

Об'єкт класу Account, ініціалізуємий конструктором, можна об'явити в такий спосіб:

Account newAcct ("Mikey Matz", 0);

Якщо ж є багато рахунків, для яких початковий баланс дорівнює 0, то корисно мати конструктор, що задає тільки ім'я власника й автоматично ініцілізує _balance нулем. Один зі способів зробити це - надати конструктор виду:

Account (const char *name);

Інший спосіб - включити в конструктор із двома параметрами значення за замовчуванням, рівне нулю:

Account (const char *name, double open_balance = 0.0);

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

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

class Account {

public:

// конструктор за замовчуванням...

Account ();

// імена параметрів в оголошенні вказувати необов'язково

Account (const char*, double=0.0);

const char* name () { return name; }

// ...

private:

// ...

};

Нижче наведені два приклади правильного визначення об'єкта класу Account, де конструкторові передається один або два аргументи:

int main ()

{

// правильно: в обох випадках викликається конструктор

// с двома параметрами

Account acct ("Ethan Stern");

Account *pact = new Account ("Michael Lieberman", 5000);

if (strcmp (acct. name (), pact->name ()))

// ...

}

C++ вимагає, щоб конструктор застосовувався до певного об'єкта до його першого використання. Це означає, що як для acct, так і для об'єкта, на який указує pact, конструктор буде викликаний перед перевіркою в інструкції if.

Компілятор перебудовує нашу програму, вставляючи виклики конструкторів.

От як, цілком ймовірно, буде модифіковане визначення acct усередині main ():

// псевдокод на C++,

// іллюструючий внутрішню вставку конструктора

int main ()

{

Account acct;

acct. Account:: Account ("Ethan Stern", 0.0);

// ...

}

Звичайно, якщо конструктор визначений як вбудований, то він підставляється в точці виклику.

Обробка оператора new трохи складніше. Конструктор викликається тільки тоді, коли він успішно виділив пам'ять. Модифікація визначення pact у трохи спрощеному виді виглядає так:

// псевдокод на C++,

// іллюструючий внутрішню вставку конструктора при обробці new

int main ()

{

// ...

Account *pact;

try {

pact = _new (sizeof (Account));

pact->Acct. Account:: Account (

"Michael Liebarman", 5000.0);

}

catch (std:: bad_alloc) {

// оператор new закінчився невдачею:

// конструктор не викликається

}

// ...

}

Існує три в загальному випадку еквівалентні форми завдання аргументів конструктора:

// загалом ці конструктори еквівалентні

Account acct1 ("Anna Press");

Account acct2 = Account ("Anna Press");

Account acct3 = "Anna Press";

Форма acct3 може використовуватися тільки при завданні єдиного аргументу. Якщо аргументів два або більше, рекомендовано користуватися формою acct1, хоча припустимо й acct2.

// рекомендує форма, що, виклику конструктора

Account acct1 ("Anna Press");

Визначати об'єкт класу, не вказуючи списку фактичних аргументів, можна в тому випадку, якщо в ньому або об'явлений конструктор за замовчуванням, або взагалі немає об'яв конструкторів. Якщо в класі об'явлений хоча б один конструктор, то не дозволяється визначати об'єкт класу, не викликаючи жодного з них. Зокрема, якщо в класі визначений конструктор, що приймає один або більше параметрів, але не визначений конструктор за замовчуванням, то в кожному визначенні об'єкта такого класу повинні бути присутнім необхідні аргументи. Можна заперечити, що не має змісту визначати конструктор за замовчуванням для класу Account, оскільки не буває рахунків без імені власника. У переглянутій версії класу Account такий конструктор виключений:

class Account {

public:

// імена параметрів в оголошенні вказувати необов'язково

Account (const char*, double=0.0);

const char* name () { return name; }

// ...

private:

// ...

};

Тепер при оголошенні кожного об'єкта Account у конструкторі обов'язково треба вказати як мінімум аргумент типу C-рядка, але це швидше за все безглуздо. Чому? Контейнерні класи (наприклад, vector) вимагають, щоб для класу елементів, що поміщають у них, був або заданий конструктор за замовчуванням, або взагалі ніяких конструкторів. Аналогічна ситуація має місце при виділенні динамічного масиву об'єктів класу. Так, що інструкція викликала б помилку компіляції для нової версії Account:

// помилка: потрібен конструктор за замовчуванням для класу

Account *pact = new Account [new_client_cnt];

На практиці часто потрібно задавати конструктор за замовчуванням, якщо є які-небудь інші конструктори.

А якщо для класу немає розумних значень за замовчуванням? Наприклад, клас Account вимагає задавати для будь-якого об'єкта прізвище власника рахунку.

У такому випадку найкраще встановити стан об'єкта так, щоб було видно, що він ще не ініціалізований коректними значеннями:

// конструктор за замовчуванням для класу Account

inline Account:: Account () {

_name = 0;

_balance = 0.0;

_acct_nmbr = 0;

}

Однак у функції-члени класу Account прийдеться включити перевірку цілісності об'єкта перед його використанням.

Існує й альтернативний синтаксис: список ініціалізації членів, у якому через кому вказуються імена й початкові значення. Наприклад, конструктор за замовчуванням можна переписати в такий спосіб:

// конструктор за замовчуванням класу Account з використанням

// списку ініціалізації членів

inline Account::

Account ()

: _name (0),

_balance (0.0), _acct_nmbr (0)

{}

Такий список допустимо тільки у визначенні, але не в оголошенні конструктора. Він міститься між списком параметрів і тілом конструктора й відділяється двокрапкою. От як виглядає наш конструктор із двома параметрами при частковому використанні списку ініціалізації членів:

inline Account::

Account (const char* name, double opening_bal)

: _balance (opening_bal)

{

_name = new char [strlen (name) +1];

strcpy (_name, name);

_acct_nmbr = get_unique_acct_nmbr ();

}

Конструктор не можна об'являти із ключовими словами const або volatile, тому наведені записи невірні:

class Account {

public:

Account () const; // помилка

Account () volatile; // помилка

// ...

};

Це не означає, що об'єкти класу з такими специфікаторами заборонено ініціалізувати конструктором. Просто до об'єкта застосовується підходящий конструктор, причому без обліку специфікаторів в оголошенні об'єкта. Константність об'єкта класу встановлюється після того, як робота з його ініціалізації завершена, і пропадає в момент виклику деструктора. Таким чином, об'єкт класу зі специфікатором const уважається константним з моменту завершення роботи конструктора до моменту запуску деструктора. Те ж саме ставиться й до специфікатора volatile.

Розглянемо наступний фрагмент програми:

// у якімсь заголовному файлі

extern void print (const Account &acct);

// ...

int main ()

{

// перетворить рядок "oops" в об'єкт класу Account

// за допомогою конструктора Account:: Account ("oops", 0.0)

print ("oops");

// ...

}

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

Ненавмисні неявні перетворення класів, наприклад трансформація "oops" в об'єкт класу Account, виявилися джерелом помилок, що виявляють важко. Тому в стандарт C++ було додано ключове слово explicit, що говорить компіляторові, що такі перетворення не потрібні:

class Account {

public:

explicit Account (const char*, double=0.0);

};

Даний модифікатор застосуємо тільки до конструктора.

1.8 Конструктор копіювання

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

Часто почленна ініціалізація не забезпечує коректну дію класу. Тому ми явно визначаємо конструктор копіювання. У нашому класі Account це необхідно, інакше два об'єкти будуть мати однакові номери рахунків, що заборонено специфікацією класу.

Конструктор копіювання приймає як формальний параметр посилання на об'єкт класу (рекомендовано зі специфікатором const). Його реалізація:

inline Account::

Account (const Account &rhs)

: _balance (rhs. _balance)

{

_name = new char [strlen (rhs. _name) + 1];

strcpy (_name, rhs. _name);

// копіювати rhs. _acct_nmbr не можна

_acct_nmbr = get_unique_acct_nmbr ();

}

Коли ми пишемо:

Account acct2 (acct1);

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

1.9 Деструктор класу

Одна із цілей, що ставляться перед конструктором, - забезпечити автоматичне виділення ресурсу. Ми вже бачили в прикладі із класом Account конструктор, де за допомогою оператора new виділяється пам'ять для масиву символів і привласнюється унікальний номер рахунку. Можна також представити ситуацію, коли потрібно одержати монопольний доступ до поділюваної пам'яті або до критичної секції потоку. Для цього необхідна симетрична операція, що забезпечує автоматичне звільнення пам'яті або повернення ресурсу після завершення часу життя об'єкта, - деструктор. Деструктор - це спеціальна обумовлена користувачем функція-член, що автоматично викликається, коли об'єкт виходить із області видимості або коли до покажчика на об'єкт застосовується операція delete. Ім'я цієї функції створено з імені класу з попереднім символом “тильда" (~). Деструктор не повертає значення й не приймає ніяких параметрів, а отже, не може бути перевантажений.


Подобные документы

  • Концепції об'єктно-орієнтованого програмування. Методи створення класів. Доступ до методів базового класу. Структура даних, функції. Розробка додатку на основі діалогових вікон, програми меню. Засоби розробки програмного забезпечення мовами Java та С++.

    курсовая работа [502,5 K], добавлен 01.04.2016

  • Об’єктно-орієнтоване програмування мовою С++. Основні принципи об’єктно-орієнтованого програмування. Розробка класів з використанням технології візуального програмування. Розробка класу classProgressBar. Базовий клас font. Методи тестування програми.

    курсовая работа [211,3 K], добавлен 19.08.2010

  • Поняття абстрактного класу в об’єктно-орієнтованому програмуванні. Описання класу pruzhyna, створення консольної програми для обчислення коефіцієнта передачі пружини стискання з циліндричної проволоки круглого перетину за заданою формулою і параметрами.

    курсовая работа [100,7 K], добавлен 25.11.2011

  • Поняття та види векторів. Прості математичні операції над ними. Векторний добуток, його геометричні та алгебраїчні властивості. Визначення та реалізація програмного класу багатовимірних векторів. Перевантажені оператори та дружні оператор-функції.

    курсовая работа [110,1 K], добавлен 15.01.2012

  • Розробка методу-члену класу для створення нового одновимірного масиву з кількості всіх негативних елементів кожного рядка заданого двовимірного динамічного масиву. Особливість виводу змісту масиву на екран. Аналіз перевірки правильності роботи програми.

    лабораторная работа [131,2 K], добавлен 18.11.2021

  • Редагування за допомогою текстового редактора NotePad вхідного файлу даних. Програмна реалізація основного алгоритму з використанням засобів об'єктно-орієнтованого програмування. Об’ява та опис класів і об'єктів. Розробка допоміжних програмних засобів.

    курсовая работа [69,4 K], добавлен 14.03.2013

  • Принципи об'єктно-орієнтованого підходу. Розробка програмного комплексу з використанням цього алгоритму і користувальницьких класів на мові програмування С++. Реалізація простого відкритого успадкування. Тестування працездатності системи класів.

    курсовая работа [98,0 K], добавлен 06.05.2014

  • Визначення сили взаємодії двох точкових зарядів. С++ як універсальна мова програмування. Клас - ключове поняття С++. Стандартні бібліотеки С++. Функція конструктора і деструктора. Опис базового класу Objcalc. Лістинг та результат роботи програми.

    курсовая работа [107,6 K], добавлен 27.12.2011

  • Реалізація операцій ініціалізації (завдання початкових властивостей), запису класу у файл та читання класу з файлу. Функції інформаційної системи "Фермерське господарство", модулі створення та перегляду файлу, додавання даних в файл, пошуку даних.

    курсовая работа [4,9 M], добавлен 16.03.2012

  • Розробка програми на мові програмування С++ з використанням об’єктно-орієнтованого програмування, яка включає в себе роботу з файлами, класами, обробку числової інформації і роботу з графікою. Структура класів і об’єктів. Лістинг та алгоритм програми.

    курсовая работа [104,4 K], добавлен 14.03.2013

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.