Студопедия
Новини освіти і науки:
МАРК РЕГНЕРУС ДОСЛІДЖЕННЯ: Наскільки відрізняються діти, які виросли в одностатевих союзах


РЕЗОЛЮЦІЯ: Громадського обговорення навчальної програми статевого виховання


ЧОМУ ФОНД ОЛЕНИ ПІНЧУК І МОЗ УКРАЇНИ ПРОПАГУЮТЬ "СЕКСУАЛЬНІ УРОКИ"


ЕКЗИСТЕНЦІЙНО-ПСИХОЛОГІЧНІ ОСНОВИ ПОРУШЕННЯ СТАТЕВОЇ ІДЕНТИЧНОСТІ ПІДЛІТКІВ


Батьківський, громадянський рух в Україні закликає МОН зупинити тотальну сексуалізацію дітей і підлітків


Відкрите звернення Міністру освіти й науки України - Гриневич Лілії Михайлівні


Представництво українського жіноцтва в ООН: низький рівень культури спілкування в соціальних мережах


Гендерна антидискримінаційна експертиза може зробити нас моральними рабами


ЛІВИЙ МАРКСИЗМ У НОВИХ ПІДРУЧНИКАХ ДЛЯ ШКОЛЯРІВ


ВІДКРИТА ЗАЯВА на підтримку позиції Ганни Турчинової та права кожної людини на свободу думки, світогляду та вираження поглядів



Віртуальні методи

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

 

class Item

{

public:

Item();

~Item();

// істина, якщо одиниця зберігання на руках

bool IsTaken() const;

// істина, якщо цей предмет є в бібліотеку

bool IsAvailable() const;

long GetInvNumber() const; // інвентарний номер

void Take(); // операція "узяти"

void Return(); // операція "повернути"

private:

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

long invNumber;

// зберігає стан об'єкту - узятий на руки

bool taken;

};

 

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

// видати на руки

void

TakeAnItem(Item& і)

{

if (і.IsAvailable())

і.Take();

}

 

Конкретні властивості книги будуть представлені класом Book.

 

class Book : public Item

{

public:

String Author(void) const; // автор

String Title(void) const; // назва

String Publisher(void) const; // видавництво

long YearOfPublishing(void) const; // рік випуску

String Reference(void) const; // повне посилання на книгу

private:

String author;

String title;

String publisher;

short year;

};

 

Для журналу клас Magazin надає інші відомості:

 

class Magazin : public Item

{

public:

String Volume(void) const; // том

short Number(void) const; // номер

String Title(void) const; // назва

Date DateOfIssue() const; // дата випуску

private:

String volume;

short number;

String title;

Date date;

};

 

У об'єкта класу Book є методи, безпосередньо визначені в класі Book і методи, визначені в класі Item.

 

Book b;

long in = b.GetInvNumber();

String t = b.Reference();

 

Похідний клас має доступ до методів і атрибутів базового класу, оголошених в зовнішній і захищеній частині базового класу, проте доступ до внутрішньої частини базового класу не дозволений. Припустимо, що для повного посилання на книгу вирішено використовувати інвентарний номер. Метод Reference класу Book буде выглядати наступним чином:

String

Book: : Reference (void) const

{

String result = author + "\n" + title + "\n" + String (GetInvNumber ());

return result;

}

 

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

 

String result = author + "\n"

+ title + "\n"

+ String (invNumber) ;

 

не дозволена, оскільки invNumber - внутрішній атрибут класу Item. Проте якби ми помістили invNumber в захищену частину класу:

 

class Item

{

. . .

protected:

long invNumber;

};

 

то методи класів Book і Magazin могли б безпосередньо використовувати цей атрибут.

Призначення захищеної (protected) частини класу і полягає в тому, щоб закрити доступ "ззовні" до певних змінних і методів та дозволити користуватися ними в похідному класі.

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

 

class А

{

public:

. . .

int foo();

. . .

};

class В : public А

{

public:

int foo();

void bar();

};

Void B::bar()

{

x = foo(); // викликається метод foo класу В

}

Проте метод базового класу не зникає. Просто при пошуку на ім'я foo спочатку є видимими атрибути і методи самого класу. Якби ім'я не було знайдено, почалося б перегляд імен в базовому класі, потім зовнішніх імен. В даному випадку ім'я foo існує в самому класі, тому воно і використовується.

За допомогою запису А:: foo() можна явно вказати на ім'я, визначене в класі А і тоді запис:

 

х = А::foo () ;

 

викличе метод базового класу.

Взагалі запис клас::ім'я вже багато разів нами використовувався. Цей запис пошуку імені означає, що ім'я відноситься до заданого класу.

В обох класах, успадкованих з класу Іtem є метод Title, що видає як результат заголовок книги або назву журналу. Окрім цього методу, було б корисно мати метод, що видає повну назву будь-якої одиниці зберігання. Реалізація цього методу різна, оскільки назва книги і журналу складається з різних частин. Проте вигляд методу ‑ значення, що повертається, і аргумент ‑ і його загальне значення одне і те ж. Назва - це загальна властивість всіх одиниць зберігання в бібліотеці і логічно помістити метод, що видає назву, в базовий клас.

 

class Item

{

public:

virtual String Name(void) const;

. . .

};

class Book : public Item

{

public:

virtual String Name(void) const;

. . .

};

class Magazin : public Item

{

public:

virtual String Name(void) const;

. . .

};

 

Реалізація методу Name для базового класу тривіальна: оскільки назва відома лише похідного класу і буде повертатись порожній рядок.

 

String Item::Name(void) const

{

return "";

}

Для книги назва складається з прізвища автора, назви книги, видавництва і року видання:

 

String Book::Name(void) const

{

return author + title + publisher + String(year);

}

 

У журналі повна назва складається з назви журналу, року і номера:

 

String Magazin::Name(void) const

{

return title + String(year)+ String(number);

}

 

Методи Name визначені як віртуальні за допомогою описувача virtual, що стоїть перед визначенням методу. Віртуальні методи реалізують ідею поліморфізму в мові С++. Якщо в програмі використовується вказівник на базовий клас Іtem і з його допомогою викликається метод Name:

 

Item* ptr;

. . .

String name = ptr->Name();

 

то по виклику методу неможливо визначити, яка з трьох вищенаведених реалізацій Name буде виконана. Все залежить від того, на який конкретний об'єкт вказує вказівник ptr.

 

Item* ptr;

. .

if (type == "Book")

ptr = new Book;

else if (type == "Magazin")

ptr = new Magazin;

. . .

String name = ptr->Name();

 

В даному фрагменті програми якщо змінна type позначає тип бібліотечної одиниці, була рівна "Book", то буде викликаний метод Name класу Book. Якщо ж вона була рівна "Magazin", то буде викликаний метод класу Magazin.

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

Наведемо ще один приклад віртуального методу. Припустимо в графічному редакторі при натисненні певної клавіші потрібно перемальовувати поточну форму на екрані. Форма може бути квадратом, кругом, еліпсом і т.д. Ми введемо базовий клас для всіх форм Shape. Конкретні фігури, з якими працює редактор, будуть представлені класами Square (квадрат), Circle (круг), Ellipse (еліпс), похідними від класу Shape. Клас Shape визначає віртуальний метод Draw для малювання форми на екрані.

 

class Shape

{

public:

Shape();

virtual void Draw(void);

};

// квадрат

class Square : public Shape

{

public:

Square();

virtual void Draw(void);

private:

double length; // довжина сторони

};

// круг

class Circle : public Shape

{

public:

Circle();

virtual void Draw(void);

private:

short radius;

};

 

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

 

Repaint(Shape* shape)

{

shape->Draw();

}

 

В чому різниця між віртуальними методами і перевизначенням методів?

Що змінилося, якби метод Name не був описаний як віртуальний? У такому разі рішення, який саме метод був би виконаний, ухвалене б статично, під час компіляції програми. В прикладі з методом Name вказівник на базовий клас, був би викликаний методом Name класу Item. При визначенні методу як virtual, рішення, який саме метод буде виконуватися, ухвалюється під час виконання.

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

 

func(Item item)

{

item.Name(); // викликається метод Item::Name()

}

funcl(Item& item)

{

// викликається метод типу того об'єкту, на який посилається item

Item.Name();

}

 

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

Зворотне не завжди вірне. Перетворення з базового класу до похідного не завжди можна виконати. Тому говорять, що перетворення:

 

Item* iPtr;

. . .

Book* bPtr = (Book*)iPtr;

небезпечно. Таке перетворення можна виконувати тільки тоді, коли абсолютно точно відомо, що iPtr указує на об'єкт класу Book.

 


Читайте також:

  1. Автоматизація водорозподілу на відкритих зрошувальних системах. Методи керування водорозподілом. Вимірювання рівня води. Вимірювання витрати.
  2. Агрегативна стійкість, коагуляція суспензій. Методи отримання.
  3. Адаптовані й специфічні методи дослідження у журналістикознавстві
  4. Адміністративні (прямі) методи регулювання.
  5. Адміністративні методи - це сукупність прийомів, впливів, заснованих на використанні об'єктивних організаційних відносин між людьми та загальноорганізаційних принципів управління.
  6. Адміністративні методи управління
  7. Адміністративні, економічні й інституційні методи.
  8. Адміністративно-правові (організаційно-адміністративні) методи мотивації
  9. Адміністративно-правові методи забезпечення економічного механізму управління охороною довкілля
  10. Аерометоди
  11. Активні групові методи
  12. Алгоритм розробки методичних основ бюджетування




Переглядів: 743

<== попередня сторінка | наступна сторінка ==>
 | 

Не знайшли потрібну інформацію? Скористайтесь пошуком google:

  

© studopedia.com.ua При використанні або копіюванні матеріалів пряме посилання на сайт обов'язкове.


Генерація сторінки за: 0.024 сек.