Distance dist2(dist1);
Дія копіюючого конструктора за замовчуванням зводиться до копіювання значень полів об’єкту dist1 у відповідні поля об’єкту dist2. Як це не дивно, але ідентичні дії для пари об’єктів dist1 і dist3 виконуються за допомогою операторів:
Distance dist3=dist1;
Можна подумати, що даний оператор виконує операцію присвоювання, але насправді це не так. Тут, як і в попередньому випадку викликається конструктор копіювання за замовчуванням. Обидва оператори виконують одинакові дії і рівноправні у використанні. Можемо переконатися в цьому, запустивши програму 11.6 на виконання.
Об’єкти, що повертаються функцією
В програмі 11.6 ми бачили, що об’єкти можна передавати в функцію в якості аргументів. Тепер розглянемо, як функція може повертати об’єкт у викликаючу програму. Модифікуємо попередню програму (програма 11.7)
#include <iostream.h>
#include <conio.h>
#include <bios.h>
class Distance
{private:
int feet;
float inches;
public:
Distance():feet(0),inches(0.0) //constructor bez argumentiv
{ }
//constructor z 2 argumentamy
Distance(int ft,float in):feet(ft),inches(in)
{ }
void getdist()
{cout <<"\nВведіть число футів "; cin >>feet;
cout << "дюймів"; cin>>inches;
}
void showdist()
{cout <<feet << “\’ “<< inches <<”\’’”;}
Distance add_dist(Distance); //Прототип
};
//dodavanna d2 i d3
Distance Distance::add_dist(Distance d2)
{
Distance temp;
temp.inches=inches+d2.inches;
if(temp.inches>=12.0)
{temp.inches-=12.0;
temp.feet++;}
temp.feet+=feet+d2.feet;
return temp;
}
int main()
{
clrscr();
Distance dist1,dist3;
Distance dist2(11,6.25);
dist1.getdist();
dist3=dist1.add_dist(dist2);
cout <<”\ndist1=”;dist1.showdist();
cout << “\ndist2=”;dist2.showdist();
cout << “\ndist3=”;dist3.showdist();
cout <<endl;
bioskey(0);
return 0;
}
Програма 11.7
Хоча програми 11.6 та 11.7 схожі, різниця між ними вказує на важливі аспекти роботи функцій з об’єктами.
В прикладі 11.6 два об’єкти були передані в якості аргументів в функцію add_dist() , а результат був збережений в об’єкті dist3, методом якого і є викликана функція add_dist(). В програмі 11.7 в якості аргумента в функцію add_dist() передається лише один аргумент: об’єкт dist2. dist2 додається до об’єкту dist1, до якого відноситься викликаний метод add_dist(). Результат повертається в функцію main() і присвоюється об’єкту dist3.
dist3=dist1.add_dist(dist2);
Такий запис доволі близький до природного. Ще більш спростити його можна за допомгою використання перезавантажених функцій.
Функція add_dist() з прикладу 11.7 виглядає так:
Distance Distance::add_dist(Distance d2)
{
Distance temp;
temp.inches=inches+d2.inches;
if(temp.inches>=12.0)
{temp.inches-=12.0;
temp.feet++;}
temp.feet+=feet+d2.feet;
return temp;
}
Всередині функції створюється тимчасовий об’єкт класу Distance. Цей об’єкт зберігає значення обчисленої суми доти, доки вона не буде повернута викликаючій програмі. Сума обчислюється шляхом додавання двох об’єктів класу Distance. Перший з об’єктів – dist1, по відношенню до якого функція add_dist() є методом, другий об’єкт dist2 передається в функцію у якості аргумента. Звертання до його полів з функції виглядає так: d2.feet та d2.inches. Результат додавання зберігається в об’єкті temp і звертання до його полів виглядає так: temp.feet та temp.inches. Значення об’єкту temp повертається у викликаючу програму за допомогою оператора
return temp
Викликаюча програма main() присвоює значення, повернуте функцією, об’єкту dist3. Значення змінної dist1 не змінюється, а тільки використовується функцією add_dist().
Класи, об’єкти та пам’ять
Можливо, в процесі вивчення класів та об’єктів у нас сформувалося уявлення про об’єкт як про копію класу з точки зору внутрішньої структури. Це не зовсім точно. Кожен об’єкт має незалежні поля даних, але всі об’єкти одного класу використовують ті самі методи. Методи класу створюються і поміщаються в пам’ять комп’ютера один раз – при створенні класу. Між об’єктами не виникає конкуренції за спільний ресурс, оскільки в будь-який момент виконується не більше одної функції.
Статичні дані класу
Познайомившись з тим, що кожен об’єкт класу містить свої власні дані, поглибимо своє розуміння даної концепції. Якщо поле даних класу описане з ключовим полем static, то значення цього поля буде одинаковим для всіх об’єктів даного класу. Статичні дані класу корисні в тих випадках, коли необхідно, щоб всі об’єкти включали в себе якесь одинакове значення. Статичне поле за своїми характеристиками нагадує статичну змінну: воно видиме тільки всередині класу, але час його життя співпадає з часом життя програми. Таким чином, статичне поле існує навіть тоді, коли не існує жодного об’єкта класу. Тим не менше, на відміну від статичної змінної функції, призначеної для збереження значень між викликами, статична змінна класу використовується для зберігання даних, які спільно використовуються об’єктами класу.
Програма 11.8 ілюструє просте використання статичного поля класу:
#include <iostream.h>
#include <conio.h>
#include <bios.h>
class foo
{private:
static int count; //Спільне поле
public:
foo()
{count++;} //Інкрементування при створенні
int getcount()
{return count;}
};
int foo:: count=0; //Визначення count
int main()
{clrscr();
foo f1,f2,f3;//Створення 3 обєктів
cout << "Число обєктів " <<f1.getcount()<<endl;
cout << " Число обєктів " <<f2.getcount()<<endl;
cout << " Число обєктів " <<f3.getcount()<<endl;
bioskey(0);
return 0;
}
Програма 11.8
В цьому прикладі клас foo містить єдине поле count, що має тип static int. Конструктор класу інкрементує значення поля count. В функції main() ми визначаємо 3 об’єкти класу foo. Оскільки конструктор в цьому випадку викликається тричі, інкрементування поля count теж відбувається тричі. Метод getcount() повертає значення count. Ми викликаємо цей метод для кожного з об’єктів і в кожному випадку одержуємо ту саму величину.
Статичні поля класу використовуються значно рідше, ніж автоматичні, однак існують ситуації, коли їх зручно використовувати.
Роздільне оголошення і визначення полів класу
Визначення статичних полів класу відбувається не так, як у звичайних полів. Звичайні поля оголошуються (компілятору повідомляється ім’я і тип поля) і визначаються (компілятор виділяє пам’ять для збереження поля) за допомогою одного оператора. Для статичних полів ці дві дії виконуються двома різними операторами: оголошення поля знаходиться всередині визначення класу, а визначення поля, як правило, розміщується поза класом і часто являє собою оголошення глобальної змінної.
static int count; //оголошення статичного поля
int foo:: count=0; //Визначення cтатичного поля
Для чого використовується така двоїста форма? Якби означення статичного поля знаходилося всередині класу, то це порушило б принцип, згідно з яким визначення класу не повинне бути зв’язаним з виділенням пам’яті. Помістивши визначення статичного поля поза класом, ми забезпечили однократне виділення пам’яті під це поле до того, як програма буде запущена на виконання і статичне поле в цьому випадку стане доступним всьому класу. Кожен об’єкт класу вже не буде володіти своїм власним екземпляром поля, як це повинне бути з полями автоматичного типу. В цьому сенсі статичні поля нагадують глобальні змінні.
Працюючи зі статичними даними класу, легко зробити помилки, які не розпізнаються компілятором. Якщо оголосити статичне поле класу, але забути його визначити, компілятор не видасть попередження. Програма вважатиметься коректною, доки редактор зв’язків не виявить помилки і не видасть повідомлення про те, що ми збиралися звернутися до неоголошеної глобальної змінної.
Константні методи
Константні методи відрізняються тим, що не міняють значень полів свого класу. Розглянемо це на прикладі:
class aclass
{private:
int alpha;
public:
void nonfunc() //неконстантний метод
{alpha=99;} //коректно
void confiunc() const //константний метод
{alpha=99;} //некоректно: не можна міняти значення поля
};
Звичайний метод nonfunc() може поміняти значення поля, а константний метод confunc() не може. Якщо константний метод спробує поміняти значення поля, компілятор видасть повідомлення про помилку.
Для того, щоб зробити функцію константною, необхідно вказати ключове слово const після прототипу функції, але до початку тіла функції. Якщо оголошення і визначення функції розділені, то модифікатор const необхідно вказати двічі – як при оголошенні функції, так і при її визначенні. Ті методи, які тільки зчитують дані з поля класу, є сенс робити константними, оскільки у них нема необхідності змінювати значення полів об’єктів класу.
Використання константних функцій допомагає компілятору виявляти помилки. Ще за їх допомогою можна створювати і використовувати константні об’єкти.
Проілюструємо це на прикладі, оголосивши константними деякі методи класу Distance програми 11.7. Одержимо програму 11.9
#include <iostream.h>
#include <conio.h>
#include <bios.h>
class Distance
{private:
int feet;
float inches;
public:
Distance():feet(0),inches(0.0) //конструктор без аргументів
{ }
//конструктор з 2 aргументами
Distance(int ft,float in):feet(ft),inches(in)
{ }
void getdist()
{cout <<"\nВведіть число футів "; cin >>feet;
cout << "дюймів "; cin>>inches;
}
void showdist() const
{cout <<feet << "\' "<< inches <<"\''";}
Distance add_dist(const Distance)const; //Прототип
};
//dodavanna d2 i d3
Distance Distance::add_dist(const Distance d2)const
{
Distance temp;
//feet=0; //помилка не можна міняти поле
//d2.feet=0; //помилка не можна міняти поле
temp.inches=inches+d2.inches;
if(temp.inches>=12.0)
{temp.inches-=12.0;
temp.feet++;}
temp.feet+=feet+d2.feet;
return temp;
}
int main()
{
clrscr();
Distance dist1,dist3;
Distance dist2(11,6.25);
dist1.getdist();
dist3=dist1.add_dist(dist2);
cout <<"\ndist1=";dist1.showdist();
cout << "\ndist2=";dist2.showdist();
cout << "\ndist3=";dist3.showdist();
cout <<endl;
bioskey(0);
return 0;
}
Програма 11.9
В цьому прикладі обидві функції showdist() та add_dist() є константними. В тілі функції add_dist() перший із закоментованих операторів feet=0; демонструє, що компілятор видасть помилку при спробі зміни константною функцією полів об’єкта, з якого вона викликалася.
Константним можна оголосити і аргумент методу. В нашому прикладі це виглядає так:
Distance Distance::add_dist(const Distance d2)const
Функція не може поміняти значення цього аргумента, що і видно на прикладі другого закоментованого оператора d2.feet=0;
Константні об’єкти
В кількох попередніх прикладах ми бачили, що ключове слово const можна використовувати для захисту від зміни значень змінних стандартних типів, наприклад, int. Виявляється, аналогічним способом можна застосовувати модифікатор const і для об’єктів класів. Якщо об’єкт класу оголошений з модифікаторм const, він стає недоступним для зміни. Це означає, що для такого об’єкта можна викликати тільки константні методи, оскільки лише вони гарантують, що об’єкт не буде змінений. В якості прикладу розглянемо програму 11.10.
#include <iostream.h>
#include <conio.h>
#include <bios.h>
class Distance
{private:
int feet;
float inches;
public:
//Конструктор з 2 аргументами
Distance(int ft,float in):feet(ft),inches(in)
{ }
void getdist() //Неконстантний метод
{cout <<"\nВведіть число футів "; cin >>feet;
cout << "дюймів "; cin>>inches;
}
void showdist() const //Константний метод
{cout <<feet << "\' "<< inches <<"\''";}
};
int main()
{
clrscr();
Дата добавления: 2015-08-26; просмотров: 721;