Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

Namespace CounterNameSpace 2 страница. strcpy(tmp.string, string);




strcpy (string, str);

strcpy (tmp. string, string);

 

return tmp; // Повертає модифікований тимчасовий об'єкт

}

 

// Конкатенація рядка з об'єктом типу strClass, що завершується нулем

strClass strClass:: operator +(char *str)

{

strClass tmp; // Створення тимчасового об'єкта

strcpy (tmp. string, string);

strcpy (tmp. string, str);

 

return tmp; // Повертає модифікований тимчасовий об'єкт

}

Уважно проаналізуйте коди цих функцій. Зверніть увагу на те, що правий аргумент є не об'єктом типу strClass, а покажчиком на символьний масив, який завершується нулем, тобто звичайним С++-рядком. Але обидві ці функції повертають об'єкт типу strClass. І хоча теоретично вони могли б повертати об'єкт будь-якого іншого типу, весь сенс їх існування і полягає у тому, щоб повертати об'єкт типу strClass, оскільки результати цих операцій приймаються також об'єктами типу strClass. Перевага визначення рядкової операції, у якій як правий операнд бере участь рядок, що завершується нульовим символом, полягає у тому, що воно дає змогу писати деякі настанови в природній формі. Наприклад, наведені нижче настанови є цілком законними:

strClass а, b, c;

a = "Привіт усім"; // Присвоєння рядка, що завершує нулем, об'єкту

c = а + "Георгій"; //Конкатенація об'єкта з рядком, що завершується нулем

Наведений нижче код програми містить додаткові визначення операторів "=" і "+".

Код програми 14.13. Демонстрація варіанта удосконалення рядкового класу

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

#include <cstring> // Для роботи з рядковими типами даних

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

 

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

char string [80];

public:

strClass(char *str = "") { strcpy (string, str);}

 

strClass operator +(strClass s_ob);

strClass operator +(char *str);

 

strClass operator =(strClass s_ob);

strClass operator =(char *str);

void showStr() { cout << string;}

};

 

strClass strClass:: operator +(strClass s_ob)

{

strClass tmp; // Створення тимчасового об'єкта

strcpy (tmp. string, string);

strcpy (tmp. string, s_ob. string);

 

return tmp; // Повертає модифікований тимчасовий об'єкт

}

 

strClass strClass:: operator =(strClass s_ob)

{

strcpy (string, s_ob. string);

// Повернення модифіков. об'єкта операнда, адресованого покажчиком

return * this;

}

 

strClass strClass:: operator =(char *str)

{

strClass tmp; // Створення тимчасового об'єкта

strcpy (string, str);

strcpy (tmp. string, string);

 

return tmp; // Повертає модифікований тимчасовий об'єкт

}

 

strClass strClass:: operator +(char *str)

{

strClass tmp; // Створення тимчасового об'єкта

strcpy (tmp. string, string);

strcpy (tmp. string, str);

 

return tmp; // Повертає модифікований тимчасовий об'єкт

}

 

int main ()

{

strClass A_ob("Привіт "), B_ob("всім"), C_ob;

 

C_ob = A_ob + B_ob;

C_ob.showStr(); cout << "\n";

A_ob = "для програмування, тому що";

A_ob.showStr(); cout << "\n";

B_ob = C_ob = "C++ це супер";

C_ob = C_ob + " " + A_ob + " " + B_ob;

C_ob.showStr();

 

getch (); return 0;

}

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

Привіт усім

для програмування, тому що

C++ це супер для програмування, тому що C++ це супер

Перш ніж переходити до наступного розділу, переконайтеся у тому, що до кінця розумієте, як отримано ці результати. Тепер спробуйте самостійно визначати інші операції над рядками. Також, спробуйте, наприклад, визначити операцію видалення підрядка на основі оператора "-". Так, якщо рядок об'єкта А містить фразу "Це важкий-важкий тест", а рядок об'єкта В – фразу "важкий", то обчислення виразу А-В| дасть у підсумку "Це – тест". У цьому випадку з початкового рядка були видалені всі входження підрядка "важкий". Визначте також "дружню" функцію, яка б давала змогу рядку, що завершується нулем, знаходитися зліва від оператора додавання "+". Нарешті, додайте у програму код, який забезпечує контроль за помилками.

Варто знати! Для створюваних власних класів завжди є сенс експериментувати з перевантаженням операторів. Як показують приклади цього розділу, механізм перевантаження операторів можна використовувати для додавання нових типів даних у середовище програмування. Це один з найпотужніших засобів мови програмування C++.


Розділ 15. Успадкування в класах

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

У стандартній термінології мови програмування C++ клас, який успадковується, називається базовим. Клас, який успадковує базовий клас, називається похідним. Похідний клас можна використовувати як базовий для іншого похідного класу. Таким шляхом і будується багаторівнева ієрархія класів.

15.1. Поняття про успадкування

Мова програмування C++ підтримує механізм успадкування, даючи змогу в оголошенні класу вбудовувати інший клас. Для цього базовий клас задається під час оголошення похідного. Щоб зрозуміти сказане, краще почнемо з конкретного прикладу. Розглянемо клас dorZasib, який у найзагальніших рисах визначає дорожній транспортний засіб. Його члени даних дають змогу зберігати наявну кількість коліс і можливу кількість пасажирів, яких може перевозити транспортний засіб:

// Оголошення класу, що визначає дорожній транспортний засіб

class dorZasib {

int kolesa; // Кількість коліс

int pasagyry; // Кількість пасажирів

public:

void setKolesa(int kzm) { kolesa = kzm;}

int getKolesa() { return kolesa;}

void setPasagyry(int p) { pasagyry = p;}

int getPasagyry() { return pasagyry;}

};

Це загальне визначення дорожнього транспортного засобу можна використовувати для| визначення будь-якого конкретного типу автотранспортну. Наприклад, у наведеному нижче коді програми шляхом успадкування класу dorZasib створює клас vanAuto (вантажних автомобілів):

class vanAuto: public dorZasib {

int mistkist; // вантажомісткість у м куб.

public:

void setMistkist(int mzm) { mistkist = mzm;}

int getMistkist() { return mistkist;}

void showRez();

};

Той факт, що клас vanAuto успадковує клас dorZasib, означає, що клас vanAuto успадковує весь вміст класу dorZasib. До вмісту класу dorZasib клас vanAuto додає члена даних mistkist, а також функції-члени, необхідні для підтримки члена mistkist.

Базовий клас успадковується похідним класом.

Звернемо Вашу увагу на те, як успадковується клас dorZasib. Загальний формат для забезпечення успадкування має такий вигляд:

class ім'я_похідного_класу: доступ ім'я_базового_класу

{

тіло нового класу

}

У цьому оголошенні елемент доступ є необов'язковим. У разі потреби він може бути виражений одним із специфікаторів доступу: public, private або protected. Грунтовніше про ці специфікатори доступу можна дізнатися нижче у цьому розділі. А поки що у визначеннях усіх успадкованих класів будемо використовувати специфікатор public. Це означає, що всі public -члени базового класу також будуть public -членами похідного класу. Отже, у попередньому прикладі члени класу vanAuto мають доступ до відкритих функцій-членів класу dorZasib, неначе вони (ці функції) були оголошені в тілі класу vanAuto. Проте клас vanAuto не має доступу до private -членів класу dorZasib. Наприклад, для класу vanAuto закритий доступ до члена даних kolesa.

Розглянемо програму, яка використовує механізм успадкування для створення двох підкласів класу dorZasib: vanAuto і lehAuto.

Код програми 15.1. Демонстрація механізму застосування успадкування

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

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

 

// Оголошення базового класу транспортних засобів

class dorZasib {

int kolesa; // Кількість коліс

int pasagyry; // Кількість пасажирів

public:

void setKolesa(int kzm) { kolesa = kzm;}

int getKolesa() { return kolesa;}

void setPasagyry(int p) { pasagyry = p;}

int getPasagyry() { return pasagyry;}

};

 

// Оголошення похідного класу вантажівок.

class vanAuto: public dorZasib {

int mistkist; // вантажомісткість у м куб.

public:

void setMistkist(int mzm) { mistkist = mzm;}

int getMistkist() { return mistkist;}

void showRez();

};

 

enum type {car, van, wagon};

 

// Оголошення похідного класу автомобілів.

class lehAuto: public dorZasib {

enum type carType;

public:

void setType(type t) { carType = t;}

enum type getType() { return carType;}

void showRez();

};

 

void vanAuto::showRez()

{

cout << "коліс: " << getKolesa() << "\n";

cout << "пасажирів: " << getPasagyry() << "\n";

cout << "вантажомісткість у м куб.: " << mistkist << "\n";

}

 

void lehAuto::showRez()

{

cout << "коліс: " << getKolesa() << "\n";

cout << "пасажирів: " << getPasagyry() << "\n";

cout << "тип: ";

switch (getType()) {

case van: cout << "автофургон\n";

break;

case car: cout << "легковий\n";

break;

case wagon: cout << "фура\n";

}

}

 

int main ()

{

vanAuto T_ob, F_ob;

lehAuto G_ob;

 

// Ініціалізація об'єкта типу вантажівка

T_ob.setKolesa(18);

T_ob.setPasagyry(2);

T_ob.setMistkist(160);

 

// Ініціалізація об'єкта типу вантажівка

F_ob.setKolesa(6);

F_ob.setPasagyry(3);

F_ob.setMistkist(80);

 

// Виведення інформації про об'єкт типу вантажівка

T_ob.showRez();

cout << "\n";

F_ob.showRez();

cout << "\n";

 

// Ініціалізація об'єкта типу автомобіль

G_ob.setKolesa(4);

G_ob.setPasagyry(6);

G_ob.setType(van);

 

// Виведення інформації про об'єкт типу автомобіль

G_ob.showRez();

 

getch (); return 0;

}

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

коліс: 18

пасажирів: 2

вантажомісткість у м куб.: 160

 

коліс: 6

пасажирів: 3

вантажомісткість у м куб.: 80

 

коліс: 4

пасажирів: 6

тип: автофургон

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

Варто знати! Звернемо Вашу увагу на те, що обидва класи vanAuto і lehAuto містять функцію-члена showRez(), яка відображає інформацію про об'єкт. Ця функція демонструє ще один аспект об'єктно-орієнтованого програмування – поліморфізм. Оскільки кожна функція showRez() пов'язана з власним класом, компілятор може легко "зрозуміти", яку саме функцію потрібно викликати для даного об'єкта.

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

15.2. Керування доступом до членів базового класу

Якщо один клас успадковує інший, то члени базового класу стають членами похідного. Статус доступу до членів базового класу у похідному класі визначається специфікатором доступу, що використовують для успадкування базового класу. Специфікатор доступу базового класу виражається одним з ключових слів: public, private або protected. Якщо специфікатор доступу не вказано, то за замовчуванням використовується специфікатор private, коли йдеться про успадкування типу class. Якщо ж успадковується тип struct, то за відсутності безпосередньо заданого специфікатора доступу використовується специфікатор public.

Розглянемо раміфікацію (розгалуження) використання специфікаторів public або private [61] (рис. 15.1).

Рис. 15.1. Керування доступом до членів базового класу

Якщо базовий клас успадковується як public -клас, та всі його public -члени стають public -членами похідного класу.

В усіх випадках private -члени базового класу залишаються закритими у межах цього класу і не доступні для членів похідного. Наприклад, у наведеному нижче коді програми public -члени класу baseClass стають public -членами класу derived. Отже, вони будуть доступними і для інших частин програми.

Код програми 15.2. Демонстрація механізму доступу до членів базового класу після їх успадковується як public-клас

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

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

 

class baseClass { // Оголошення базового класу

int izm, jzm;

public:

void setBase(int a, int b) { izm = a; jzm = b;}

void showBase() { cout << "izm= " << izm

<< "; jzm= " << jzm << "\n";}

};

 

// Оголошення похідного класу

class derived: public baseClass {

int kzm;

public:

derived(int x) { kzm = x;}

void showKzm() { cout << "kzm= " << kzm << "\n";}

};

 

int main ()

{

derived D_ob(3);

 

D_ob.setBase(1, 2); // Доступ до членів класу baseClass

D_ob.showBase(); // Доступ до членів класу baseClass

 

D_ob.showKzm(); // Доступ до члена класу derived

 

getch (); return 0;

}

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

izm= 1; jzm= 2

kzm= 3

Оскільки функції setBase() і showBase() (члени класу baseClass) успадковані класом derived як public -члени, то їх можна викликати для об'єкта типу derived у функції main (). Позаяк члени даних izm та jzm визначені як private -члени, то вони залишаються закритими у межах свого класу baseClass.

Протилежністю відкритому (public) успадкуванню є закрите (private).

Якщо базовий клас успадковується як private -клас, то всі його public -члени стають private -членами похідного класу.

Наприклад, наведений нижче код програми не скомпілюється, оскільки обидві функції setBase() і showBase() тепер стали private -членами класу derived, і тому їх не можна викликати з функції main ().

Код програми 15.3. Демонстрація механізму доступу до членів базового класу після їх успадковується як private-клас

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

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

 

class baseClass { // Оголошення базового класу

int izm, jzm;

public:

void setBase(int a, int b) { izm = a; jzm = b;}

void showBase()

{ cout << "izm= " << izm << "; jzm= " << jzm << "\n";}

};

 

// Відкриті члени класу baseClass тепер стають

// Закритими членами класу derived.

class derived: private baseClass {

int kzm;

public:

derived(int x) { kzm = x;}

void showKzm() { cout << "kzm= " << kzm << "\n";}

};

 

int main ()

{

derived D_ob(3);

 

D_ob.setBase(1, 2); // Помилка, доступу до функції setBase() немає.

D_ob.showBase(); // Помилка, доступу до функції showBase() немає.

 

getch (); return 0;

}

Варто знати! У випадку, коли базовий клас успадковується як private -клас, то його відкриті члени стають закритими (private) членами похідного класу. Це означає, що вони доступні для членів похідного класу, але не доступні для інших частин програми.

15.3. Використання захищених членів класу

Член класу може бути оголошений не тільки відкритим (public) або закритим (private), але і захищеним (protected). Окрім того, базовий клас у цілому може успадковуватися з використанням специфікатора protected. Ключове слово protected додане мові програмування C++ для надання механізму успадкування більшої гнучкості.

Специфікатор доступу protected оголошує захищені члени або забезпечує успадкування захищеного класу.

Якщо член класу оголошений з використанням специфікатора доступу protected, то він не буде доступним для інших елементів програми, які не є членами даного класу. За одним важливим винятком доступ до захищеного члена є ідентичним доступу до закритого члена, тобто до нього можуть звертатися тільки інші члени того ж самого класу. Єдиний виняток з цього правила проявляється під час успадкування захищеного члена, тобто захищений член істотно відрізняється від закритого.

15.3.1. Використання специфікатора доступу protected для надання членам класу статусу захищеності

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

Код програми 15.4. Демонстрація механізму використання захищених членів класу

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

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

 

class baseClass { // Оголошення базового класу

protected:

int izm, jzm; // Ці члени закриті у класі baseClass

// але доступні для класу derived.

public:

void setBase(int a, int b) { izm = a; jzm = b;}

void showBase()

{ cout << "izm= " << izm << "; jzm= " << jzm << "\n";}

};

 

// Оголошення похідного класу

class derived: public baseClass {

int kzm;

public:

// Клас derived має доступ до членів класу baseClass izm та jzm.

void setKzm() { kzm = izm*jzm;}

void showKzm() { cout << "kzm= " << kzm << "\n";}

};

 

int main ()

{

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

 

D_ob.setBase(2, 3);// OK, класу derived це робити дозволено.

D_ob.showBase(); // OK, класу derived це робити дозволено.

 

D_ob.setKzm(); // Функція setKzm() належить класу derived.

D_ob.showKzm(); // Функція showKzm() належить класу derived.

 

getch (); return 0;

}

Оскільки клас baseClass успадкований класом derived відкритим способом (тобто як public -клас), а члени izm та jzm оголошені захищеними у класі baseClass, то функція setKzm() (член класу derived) може отримувати до них доступ. Якби члени izm та jzm були оголошені у класі baseClass закритими, то клас derived не міг би звертатися до них і ця програма не скомпілювалася б.

Необхідно пам'ятати! Специфікатор protected дає змогу створити член класу, який буде доступним у рамках даної ієрархії класів, але є закритим для решти елементів програми.

Якщо деякий похідний клас використовується як базовий для іншого похідного класу, то будь-який захищений член початкового базового класу, який успадковується (відкритим способом) першим похідним класом, може успадковуватися ще раз (як захищений член) другим похідним класом. Наприклад, у наступній (цілком коректній) програмі клас derivedB має законний доступ до членів izm та jzm.

Код програми 15.5. Демонстрація механізму доступу до захищених членів

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

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

 

class baseClass { // Оголошення базового класу

protected:

int izm, jzm;

public:

void setBase(int a, int b) { izm = a; jzm = b;}

void showBase()

{ cout << "izm= " << izm << "; jzm= " << jzm << "\n";}

};

 

// Члени izm та jzm успадковуються як protected -члени.

class derivedA: public baseClass {

int kzm;

public:

void setKzm() { kzm = izm*jzm;} // Правомірний доступ

void showKzm() { cout << "kzm= " << kzm << "\n";}

};

 

// Члени izm та jzm успадковуються опосередковано через клас derivedA.

class derivedB: public derivedA {

int mzm;

public:

void setMzm() { mzm = izm + jzm;} // правомірний доступ

void showMzm() { cout << "mzm= " << mzm << "\n";}

};

 

int main ()

{

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

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

 

A_ob.setBase(2, 3);

A_ob.showBase(); // izm= 2; jzm= 3

A_ob.setKzm();

A_ob.showKzm(); // kzm= 6

 

B_ob.setBase(3, 4);

B_ob.showBase(); // izm= 3; jzm= 4

 

B_ob.setKzm();

B_ob.setMzm();

B_ob.showKzm(); // kzm= 12

B_ob.showMzm(); // mzm= 7

 

getch (); return 0;

}

Якщо базовий клас успадковується закритим способом (тобто з використанням специфікатора private), то захищені (derived) члени цього базового класу стають закритими (private) членами похідного класу. Отже, якби у попередньому прикладі клас baseClass успадковувався закритим способом, то всі його члени стали б private -членами класу derivedA, і у цьому випадку вони не були б доступні для класу derivedB[62]. Ця ситуація продемонстровано у наведеному нижче коді програми, яка через це є некоректною і не скомпілюється. Всі помилки відзначені у коментарях.

Код програми 15.6. Демонстрація некоректного використання закритих членів

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

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

 

class baseClass { // Оголошення базового класу

protected:

int izm, jzm;

public:

void setBase(int a, int b) { izm = a; jzm = b;}

void showBase()

{ cout << "izm= " << izm << "; jzm= " << jzm << "\n";}




Поделиться с друзьями:


Дата добавления: 2014-11-29; Просмотров: 435; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.007 сек.