Студопедия

КАТЕГОРИИ:


Архитектура-(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)

Sizeof value 7 страница




У наведеному нижче прикладі показано еквівалентну програму, яка використовує замість типу struct тип class.

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

#include <vcl>

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

#include <conio> // Для консольного режиму роботи

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

 

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

int izm; // Закритий член за замовчуванням

public:

int getIzm();

void putIzm(int jzm);

};

 

int buildClass::getIzm()

{

return izm;

}

void buildClass::putIzm(int jzm)

{

izm = jzm;

}

 

int main ()

{

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

 

S_ob.putIzm(10);

cout << "izm= " << S_ob.getIzm() << "\n";

 

getch (); return 0;

}

Іноді С++-програмісти до структур, які не містять функцій-членів, застосовують термін POD- struct.

С++-програмісти тип class використовують в основному для визначення форми об'єкта, який містить функції-члени, а тип struct – для побудови об'єктів, які містять тільки члени даних. Іноді для опису структури, яка не містить функцій-членів, використовують абревіатуру POD (Plain Old Data).

Порівняння структур з класами

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

Відповідь на цю низку міркувань знаходимо, насамперед, у походженні мови програмування C++ від мови С, а також у намірі зберегти мову програмування C++ сумісною з мовою С. Відповідно до сучасного визначення C++-стандартна С-структура одночасно є абсолютно законною С++-структурою. У мові С, яка не містить ключових слів public або private, всі члени структури є відкритими. Ось чому і члени С++-структур за замовчуванням є відкритими (а не закритими). Оскільки конструкція типу class спеціально розроблена для підтримки інкапсуляції, то є певний сенс у тому, щоб за замовчуванням її члени були закритими. Отже, щоб уникнути несумісності з мовою С у цьому питанні, аспекти доступу, що діють за замовчуванням, змінювати було неможливо, тому і вирішено було додати нове ключове слово. Але в перспективі можна сподіватися на дещо вагомішу причину відділення структур від класів. Позаяк тип class синтаксично відокремлений від типу struct, то визначення класу цілком відкрите для еволюційних змін, які синтаксично можуть виявитися несумісними з С-подібними структурами. Оскільки ми маємо справу з двома окремими типами, то майбутній напрям розвитку мови програмування C++ не обтяжується "моральними зобов'язаннями", пов'язаними з сумісністю з С-структурами.

На завершення цієї теми зазначимо таке. Структура визначає тип класу. Отже, структура є класом. На цьому наполягав винахідник мови програмування C++ Б'єрн Страуструп. Він вважав: якщо структура і класи будуть більш-менш еквівалентними, то перехід від мови С до мови програмування C++ стане простішим. Історія розвитку мови С++ довела його правоту!

12.5. Об'єднання і класи – споріднені типи

Той факт, що структури і класи – споріднені, зазвичай нікого не дивує; проте Ви можете здивуватися, дізнавшись, що об'єднання також пов'язані з класами "близькими відносинами". Згідно з визначенням мови програмування C++, об'єднання – це, по суті, аналогічний клас, у якому всі члени даних зберігаються в одній і тій самій області[47]. Об'єднання може містити конструктор і деструктор, а також функції-члени. Звичайно ж, члени об'єднання за замовчуванням відкриті (public), а не закриті (private).

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

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

#include <vcl>

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

#include <conio> // Для консольного режиму роботи

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

 

union demoUnion { // Оголошення типу об'єднання

demoUnion(short int a); // Відкриті члени за замовчуванням

void showChars();

short int izm;

char ch[2];

};

 

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

demoUnion::demoUnion(short int a)

{

izm = a;

}

 

// Відображення символів, які становлять значення типу short int.

void demoUnion::showChars()

{

cout << ch[0] << " ";

cout << ch[1] << "\n";

}

 

int main ()

{

demoUnion U_ob(1000); // Створення об'єкта об'єднання та надання

// йому числового значення

U_ob.showChars();

 

getch (); return 0;

}

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

12.6. Вбудовувані функції

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

inline int fun_f()

{

//...

}

Модифікатор inline повинен передувати всій решті аспектів оголошення функції.

Вбудовувана функція – це невелика (за обсягом коду програми) функція, програмний код якої підставляється замість її виклику.

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

Продемонструємо використання вбудовуваної функції на прикладі такої програми.

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

#include <vcl>

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

#include <conio> // Для консольного режиму роботи

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

 

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

int izm; // Закритий член за замовчуванням

public:

int getIzm();

void putIzm(int jzm);

};

 

inline int buildClass::getIzm()

{

return izm;

}

 

inline void buildClass::putIzm(int jzm)

{

izm = jzm;

}

 

int main ()

{

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

 

S_ob.putIzm(10);

cout << "izm= " << S_ob.getIzm() << "\n";

 

getch (); return 0;

}

У цій програмі замість виклику функцій getIzm() і putIzm() підставляють їх код. Так, у функції main () рядок

S_ob.putIzm(10);

функціонально еквівалентний такій настанові присвоєння:

S_ob.izm = 10;

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

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

● деякі компілятори не генерують вбудовуваного коду, якщо відповідна функція містить цикл, конструкцію switch або настанову goto;

● найчастіше вбудовуваними не можуть бути рекурсивні функції;

● як правило, вбудовування "не проходить" для функцій, які містять статичні (static) змінні.

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

Використання вбудовуваних функцій у визначенні класу

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

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

#include <vcl>

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

#include <conio> // Для консольного режиму роботи

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

 

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

int izm; // Закритий член за замовчуванням

public:

// Автоматично вбудовувані функції

int getIzm() { return izm;}

void putIzm(int jzm) { izm = jzm;}

};

 

int main ()

{

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

 

S_ob.putIzm(10);

cout << "izm= " << S_ob.getIzm() << "\n";

 

getch (); return 0;

}

У цій програмі функції getIzm() і putIzm() визначені в тілі оголошення класу buildClass і автоматично є вбудовуваними.

Звернемо Вашу увагу на те, як виглядають коди функцій, визначених "усередині" класу buildClass. Для дуже невеликих за обсягом функцій таке представлення коду програми відображає звичайний стиль мови програмування C++. Проте ці функції можна сформатувати так:

// Альтернативний запис вбудовуваних функцій у визначенні класу

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

int izm; // Закритий член за замовчуванням

public:

// Вбудовувані функції

int getIzm()

{

return izm;

}

 

void putIzm(int jzm)

{

izm = jzm;

}

};

У загальному випадку невеликі функції (які представлено у наведеному прикладі) визначаються в оголошенні класу. Ця домовленість стосується і решти прикладів цього навчального посібника.

Варто знати! Визначення невеликих функцій-членів класу в оголошенні класу – звичайна практика в С++-програмуванні. Тут справа навіть не в засобі автоматичного вбудовування, а просто в зручності запису коду функції. Навряд чи Вам трапиться в професійних програмах, щоб короткі функції-члени визначалися поза їх класом.

12.7. Створення масивів об'єктів

Масиви об'єктів можна створювати так само, як і створюються масиви значень інших типів. Наприклад, у наведеному нижче коді програми створюється клас displayClass, який містить значення розширення для різних режимів відеомонітора. У функції main () створюється масив для зберігання трьох об'єктів типу displayClass, а доступ до них, які є елементами цього масиву, здійснюється за допомогою звичайної процедури індексування елементів масиву.

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

#include <vcl>

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

#include <conio> // Для консольного режиму роботи

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

 

enum resolution {low, medium, high};

 

// Визначення класу displayClass.

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

int width;

int height;

resolution res;

public:

void setDim(int w, int h) {width = w; height = h;}

void getDim(int &w, int &h) {w = width; h = height;}

void setRes(resolution r) {res = r;}

resolution getRes() { return res;}

};

 

char names[3][9] = {

"Низький",

"Середній",

"Високий"

};

 

int main ()

{

displayClass ds_mode[3];

int i, w, h;

 

ds_mode[0].setRes(low);

ds_mode[0].setDim(640, 480);

 

ds_mode[1].setRes(medium);

ds_mode[1].setDim(800, 600);

 

ds_mode[2].setRes(high);

ds_mode[2].setDim(1600, 1200);

 

cout << "Можливі режими відображення даних: \n\n";

 

for (i=0; i<3; i++) {

cout << names[ds_mode[i].getRes()] << ": ";

ds_mode[i].getDim(w, h);

cout << w << " x " << h << "\n";

}

 

getch (); return 0;

}

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

Можливі режими відображення даних:

 

Низький: 640 х 480

Середній: 800 х 600

Високий: 1600 х 1200

Звернемо Вашу увагу на використання двовимірного символьного масиву names для перетворення перерахованого значення в еквівалентний символьний рядок. В усіх перерахунках, які не містять безпосередньо заданої ініціалізації, перша константа має значення 0, друга – значення 1 і т.д. Отже, значення, що повертається функцією getRes(), можна використовувати для індексації масиву names, що дає змогу вивести на екран відповідну назву режиму відображення.

Багатовимірні масиви об'єктів індексуються так само, як і багатовимірні масиви значень інших типів.

Ініціалізація масивів об'єктів

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

Код програми 12.13. Демонстрація ініціалізації масиву об'єктів

#include <vcl>

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

#include <conio> // Для консольного режиму роботи

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

 

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

int a;

public:

demoClass(int n) { a = n;}

int getAzm() { return a;}

};

 

int main ()

{

demoClass demoMas[4] = { -1, -2, -3, -4 };

int i;

 

for (i=0; i<4; i++)

cout << "demoMas[" << i << "]= "

<< demoMas[i].getAzm() << "\n";

 

getch (); return 0;

}

Результати виконання цієї програми

demoMas[0]= -1

demoMas[1]= -2

demoMas[2]= -3

demoMas[3]= -4

підтверджують, що конструктору demoClass дійсно були передані значення від -1 до -4.

Насправді синтаксис ініціалізації масиву, виражений рядком

demoClass demoMas[4] = { -1, -2, -3, -4 };,

є скороченим варіантом такого (довшого) формату:

demoClass demoMas[4] = { demoClass(-l), demoClass(-2), demoClass(-3),

demoClass(-4)};

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

Код програми 12.14. Демонстрація ініціалізації конструктором масиву об'єктів

#include <vcl>

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

#include <conio> // Для консольного режиму роботи

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

 

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

int a, b;

public:

demoClass(int n, int m) { a = n; b = m;}

int getAzm() { return a;}

int getBzm() { return b;}

};

 

int main ()

{

demoClass demoMas[4][2] = {

demoClass(1, 2), demoClass(3, 4),

demoClass(5, 6), demoClass(7, 8),

demoClass(9, 10), demoClass(11, 12),

demoClass(13, 14), demoClass(15, 16)

};

 

int i,j;

 

for (i=0; i<4; i++)

for (j=0; j<2; j++){

cout << "demoMas[" << i << "," << j << "] ==> a= ";

cout << demoMas[i][j].getAzm() << "; b= ";

cout << demoMas[i][j].getBzm() << "\n";

}

 

getch (); return 0;

}

У наведеному прикладі конструктор класу demoClass приймає два аргументи. У функції main () оголошується та ініціалізується масив demoMas шляхом безпосередніх викликів конструктора demoClass(). Для ініціалізації масиву, можна завжди використовувати довгий формат ініціалізації, навіть якщо об'єкт приймає тільки один аргумент (коротка форма просто зручніша для застосування). Неважко перевірити, що у процесі виконання ця програма відображає на екрані такі результати:

demoMas[0,0] ==> a= 1; b=2

demoMas[0,1] ==> a= 3; b=4

demoMas[1,0] ==> a= 5; b=6

demoMas[1,1] ==> a= 7; b=8

demoMas[2,0] ==> a= 9; b=10

demoMas[2,1] ==> a= 11; b=12

demoMas[3,0] ==> a= 13; b=14

demoMas[3,1] ==> a= 15; b=16

12.8. Покажчики на об'єкти

Як було показано в попередньому розділі, доступ до структури можна отримати безпосередньо або через покажчик на цю структуру. Аналогічно можна звертатися і до об'єкта: безпосередньо (як в усіх попередніх прикладах) або за допомогою покажчика на об'єкт. Щоб отримати доступ до окремого члена об'єкта виключно "силами" самого об'єкта, використовують оператор "крапка". А якщо для цього слугує покажчик на цей об'єкт, необхідно використовувати оператор "стрілка"[48].

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

Код програми 12.15. Демонстрація механізму використання покажчика на об'єкт

#include <vcl>

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

#include <conio> // Для консольного режиму роботи

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

 

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

int num;

public:

void setNum(int num) {num = num;}

void showNum() { cout << "num= " << num << "\n"; }

};

 

int main ()

{

pClass P_ob, *p; // Створення об'єкта класу і покажчика на нього.

 

P_ob.setNum(1); // Отримуємо прямий доступ до об'єкта P_ob.

P_ob.showNum();

 

p = &P_ob; // Присвоюємо покажчику p адресу об'єкта P_ob.

p->showNum(); // Отримуємо доступ до об'єкта P_ob

// за допомогою покажчика.

getch (); return 0;

}

Звернемо Вашу увагу на те, що адреса об'єкта P_ob отримується шляхом використання оператора посилання "&", що цілком відповідає механізму отриманню адреси для змінних будь-якого іншого типу.

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

Код програми 12.16. Демонстрація інкрементації та декрементації покажчика на об'єкт

#include <vcl>

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

#include <conio> // Для консольного режиму роботи

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

 

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

int num;

public:

void setNum(int num) {num = num;}

void showNum(){ cout << "num= " << num << "\n"; }

};

 

int main ()

{

pClass P_ob[2],*p;

P_ob[0].setNum(10); // Прямий доступ до об'єктів

P_ob[1].setNum(20);

 

p = &P_ob[0]; // Отримуємо покажчик на перший елемент.

p->showNum(); // Відображаємо значення елемента P_ob[0]

// за допомогою покажчика.

 

p++; // Переходимо до наступного об'єкта.

p->showNum(); // Відображаємо значення елемента P_ob[1]

// за допомогою покажчика.

 

p--; // Повертаємося до попереднього об'єкта.

p->showNum(); // Знову відображаємо значення елемента P_ob[0].

 

getch (); return 0;

}

Ось як виглядають результати виконання цієї програми:

num= 10

num= 20

num= 10

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

Посилання на об'єкти

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

 


Розділ 13. Робота з класами

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

13.1. Поняття про функції-"друзі" класу

У мові програмування C++ існує можливість доступу до закритих членів класу функціями, які не є членами цього класу. Для цього достатньо оголосити ці функції дружніми до цього класу. Щоб зробити функцію "другом" класу, потрібно помістити її прототип в public -розділ оголошення класу і попередити його ключовим словом friend. Наприклад, цей фрагмент коду функції frnd() оголошується "другом" класу demoClass:

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

//...

public:

friend void frnd(demoClass obj); // Функція-"друг" класу

//...

};

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




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


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


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



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




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