Масив вказівників на об’єкти 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Масив вказівників на об’єкти



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

В прикладі 12.18 ми створимо масив вказівників на об’єкти класу person.

 #include<iostream.h>

 #include<conio.h>

 #include<stdio.h>

 #include<bios.h>

 class person

 {private:

 char name[40];

 public:

 void SetName()

 {cout<<"Введіть імя:";cin>>name;

 }

 void PrintName()

 {cout<<"\n Імя:"<<name;

 }

};

 ///////////

 int main()

 {clrscr();

 person* persPtr[100];

 int n=0;

 char choice;

 do

 { persPtr[n]=new person;

 persPtr[n]->SetName();

 n++;

 cout<<"Continue (y/n)?";cin>>choice;

 }

 while(choice=='y');

 for(int j=0;j<n;j++)

 {cout<<"\nІнформація про N"<<j+1;

persPtr[j]->PrintName();

}

cout<<endl;

 bioskey(0);

 return 0;

 }

Програма 12.18

 

Клас person має одне поле name, що містить рядок з іменем клієнта. Методи класу SetName() та PrintName() дозволяють ввести ім’я і потім вивести його на екран.

Для доступу до методів класу використовується синтаксис

persPtr[n]->SetName();

Таким чином ми викликаємо метод SetName() для об’єкту класу person, на який вказує вказівник, що є елементом масиву persPtr.

 

Зв’язний список

Виконуючи лабораторне заняття 4-5, ми вже познайомилися з таким способом зберігання даних, як список (однозв’язний). Реалізуємо зв’язний список засобами С++.

Створимо зв’язний список, що є об’єктом класу linklist. Елементи списку представлені структурою link. Кожен елемент містить ціле число, що являє собою дані, та вказівник на наступний елемент списку. Вказівник на сам список зберігається на початку списку.

Такий список реалізується в програмі 12.19.

 #include<iostream.h>

 #include<conio.h>

 #include<stdio.h>

 #include<bios.h>

 struct link

 {int data;

 link* next;

 };

 

 class linklist //список

 {private:

 link* first;

 public:

 linclist()

 

 {first=NULL;}

 void additem(int d);

 void display();

};

 ////////////////////

 void linklist::additem(int d)

 {link* newlink=new link;

 newlink->data=d;

 newlink->next=first;

 first=newlink;

 }

 void linklist::display()

 {link* current=first;

 while(current)

 {cout<<current->data<<endl;

 current=current->next;

 }

 }

 

 ////////////////

 int main()

 {clrscr();

 linklist l1;

 l1.additem(25);

 l1.additem(35);

 l1.additem(45);

 l1.display();

 bioskey(0);

 return 0;

 }

Програма 12.19

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

 

Класи, що містять самі себе

Розглянемо можливі помилки при використанні класів та структур, що посилаються самі на себе. Структура link в прикладі 12.19 містить вказівник сама на себе. Те саме можна проробити і з класами:

class sampleclass

{ sampleclass* ptr;

};

Однак, хоча клас може містити вказівник на самого себе, сам цей об’єкт він містити не може. Запис

class sampleclass

{ sampleclass obj;

};

буде помилковим. Це саме стосується також структур.

 

 

Вказівники на вказівники

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

Завдання програми 12.20: створити масив вказівників на об’єкти класу person. Програма включає функції order() та bsort() для сортування групи об’єктів класу person, виходячи з алфавітного порядку імен.

 #include<iostream.h>

 #include<conio.h>

 #include<stdio.h>

 #include<bios.h>

 #include<string.h>

 class person

 {protected:

char* name;

 public:

 void setname()

 {cout<<”Name=”;cin>>name;}

 void printname()

 {cout<<endl<<name;}

 char* getname()

 {return name;}

 

 };

 ///////////////

 int main()

 {clrscr();

 void bsort(person**,int);//prototyp

 person* persPtr[100];

 int n=0;

 char choice;

 do

 {persPtr[n]=new person;

 persPtr[n]->setname();

 n++;

 cout<<”Continue(y/n)?”;

 cin>>choice;

 } while(choice==’y’);

 cout<<”\nНесортований список:”;

 for(int j=0;j<n;j++)

 persPtr[j]->printname();

 bsort(persPtr,n);

 cout<<”\nСортований список:”;

 for(j=0;j<n;j++)

 persPtr[j]->printname();

 cout<<endl;

 bioskey(0);

 return 0;

 }

 //////////

 void bsort(person** pp,int n)

 {void order(person**,person**);

 int j,k;

 for(j=0;j<n-1;j++)

for(k=j+1;k<n;k++)

order(pp+j,pp+k);

}

//////////

void order(person** pp1,person** pp2)

{if((*pp1)->getname()>(*pp2)->getname())

{person* tempptr=*pp1;

*pp1=*pp2;

*pp2=tempptr;

}

 

 }

Програма 12.20

 

Самостійне вивчення

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

 #include<iostream.h>

 #include<conio.h>

 #include<stdio.h>

 #include<bios.h>

 #include<string.h>

 #include<process.h>

 const int LEN=80;

 const int MAX=40;

 ///////////

 class Stack

 {private:

 char st[MAX];

 int top;

 public:

 Stack()

 {top=0;}

 void push(char var)

 {st[++top]=var;}

 char pop()

 {return st[top--];}

 int gettop()

{return top;}

};

 ///////////

 class express

 {private:

 Stack s;

 char* pStr;

 int len;

 public:

 express(char* ptr)

 {pStr=ptr;

 len=strlen(pStr);}

 void parse();

 int solve();

 };

 //////////////

 void express::parse()//add to stack

 {

 char ch;

 char lastval;//останнє значення

 char lastop;//останній оператор

 for(int j=0;j<len;j++)

 {ch=pStr[j];

 if (ch>=’0’ && ch<=’9’)//якщо цифра

 s.push(ch-‘0’);

 else

 if(ch==’+’||ch==’-‘||ch==’*’||ch==’/’)

 {if (s.gettop()==1)

 s.push(ch);

 else

 {lastval=s.pop();

 lastop=s.pop();

 if((ch==’*’||ch==’/’)&&(lastop==’+’||lastop==’-‘))

 {s.push(lastop);

 s.push(lastval);}

 else

 {switch(lastop)

 {case’+’:s.push(s.pop()+lastval);break;

case’-‘:s.push(s.pop()-lastval);break;

case’*’:s.push(s.pop()*lastval);break;

case’/’:s.push(s.pop()/lastval);break;

default: cout<<”\nНевідомий оператор”;bioskey(0);exit(1);

}

}s.push(ch);

}

}

else

{cout<<”\nНевідомий символ”;

bioskey(0);

exit(1);

}

 }

 }

 ///////////

 int express::solve()

 {char lastval;

 while(s.gettop()>1)

 {lastval=s.pop();

 switch(s.pop())

 {case ‘+’:s.push(s.pop()+lastval); break;

case ‘-‘:s.push(s.pop()-lastval); break;

case ‘*’:s.push(s.pop()*lastval); break;

case ‘/’:s.push(s.pop()/lastval); break;

 default:cout<<”\nНевідомий оператор”;bioskey(0);exit(1);

 }

 }

 return int(s.pop());

 }

 ////////////////

 int main()

 {clrscr();

 char ans;

 char string[LEN];

 cout<<”\nВведіть вираз як 2+3*4/3-2”;

 cout<<”\nЧисла з 1 цифри”;

 cout<<”\nБез пропусків та дужок”;

 do

 {cout<<”\nВираз:”;

 cin>>string;

 express* eptr=new express(string);

 eptr->parse();

 cout<<”\nРезультат:”<<eptr->solve();

 delete eptr;

 cout<<”\nContinue y/n?”;

 cin>>ans;

} while (ans==’y’);

return 0;

 }

Програма 12.21

 

 


 

Підсумок

Всі об’єкти мають адреси в пам’яті комп’ютера. Адреси – це вказівники-константи. Адресу змінної можна одержати, використовуючи операцію видобування адреси &.

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

Спеціальний тип *void означає вказівник на будь-який тип даних. Він використовується в ситуаціях, коли один і той самий вказівник повинен зберігати адреси змінних різних типів.

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

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

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

Операція new призначена для виділення потрібної кількості пам’яті в системі. Вона повертає вказівник на виділену ділянку пам’яті. Використовується для створення змінних і структур даних під час виконання програми. Операція delete вивільняє пам’ять, виділену з використанням операції new.

Якщо вказівник вказує на об’єкт, то доступ до членів об’єкту можна одержати, використовуючи операцію ->. Для доступу до членів структури використовується такий сам синтаксис.

В класах і структурах можуть міститися елементи, які є вказівниками на самі ці класи або структури. Це дозволяє створювати складні структури, такі як зв’язні списки.

Можуть існувати вказівники на вказівники. Такі змінні визначаються з використанням подвійної зірочки, наприклад, int** pptr.

 

Питання по темі

1. Вказівник – це:

а) адреса змінної

б) позначення змінної, яка буде доступна наступною

в) змінна для збереження адреси

г) тип даних для адресних змінних

 

2. Зірочка, розміщена після типу даних, означає_____. Зірочка, розміщена перед іменем змінної, означає_______

а) адресу; вказівник

б) вказівник на; значення змінної, на яку посилається вказівник

в) знак операції розіменування; знак операції одержання адреси

 

3. Вираз *test означає:

а) вказівник на змінну test

б) розіменування змінної test

в) посилання на значення змінної, на яку вказує test.

 

4. Вказівник на void може містити вказівник на

а) будь-який тип даних

б) тип void

в) всі числові типи даних

 

5. Які способи передачі аргументу дозволяють функції поміняти передані їй аргументи?

а) за значенням і за посиланням

б) за значенням і за вказівником

в) за посиланням і за вказівником

 

6. Операція new

а) повертає вказівник на змінну

б) створює змінну з іменем new

в) одержує пам’ять для нової змінної

г) дозволяє дізнатися, скільки пам’яті вільно на певний момент

 

7. Операція delete повертає операційній системі

а) значення змінної

б) пам’ять, зайняту змінною

в) адресу змінної

 

8. Нехай дано вказівник p, що вказує на об’єкт типу upperclass. Як правильно записати виклик методу exclu() цього об’єкта?

а)p->exclu()

б)p.exclu()

в)upperclass.exclu()

 

9. Нехай дано об’єкт, що є елементом масиву objarr з номером 7. Як правильно викликати метод exclu() цього об’єкта?

а)objarr[7]->exclu()

б)objarr.exclu()[7]

в)objarr[7].exclu()

 

10. Як правильно визначити масив arr з 8 вказівників, які вказують на змінні типу float?

а) float arr[8]

б) float* arr[8]

в) float& arr[8]

 

 


 

Тема 13. Віртуальні функції

 

Вступ

Віртуальні функції

Доступ до звичайних методів через вказівники

Доступ до віртуальних методів через вказівники

Пізнє зв’язування

Абстрактні класи і чисті віртуальні функції

Приклад використання віртуальних функцій

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

Віртуальні базові класи

Дружні функції

Дружні функції як мости між класами

Приклад використання дружніх функцій

Дружність і функціональний запис

Дружні класи

Статичні функції

Доступ до статичних функцій

Ініціалізація копіювання і присвоювання

Перезавантаження оператора присвоювання

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

Ефективне використання пам’яті класом String

Вказівник this

Доступ до компонентних даних через вказівник this

Використання this для повернення значення

Удосконалена програма роботи з типом String

Уникайте неправильних присвоювань

Підсумок

Питання по темі

 

Вступ

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

 

Віртуальні функції

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

Для чого взагалі потрібні віртуальні функції? Уявимо собі, що ми створили набір об’єктів різних класів (масив, стек, чергу і т.п.), але ми хочемо, щоб вони самі і дії з ними викликалися за допомогою того самого виразу. ООП надає нам таку можливість завдяки притаманному цьому підходу поліморфізму, тобто набору різноманітних форм. Поліморфізм, поряд з класами та успадковуванням, є основоположним принципом ООП.

Щоб використовувати поліморфізм, необхідно виконати певні умови. По-перше, всі класи повинні бути спадкоємцями одного базового класу, по-друге, функція має бути оголошена як віртуальна у базовому класі (на відміну від Паскалю, в С++ достатньо оголосити про віртуальність методу один раз).

 



Поделиться:


Последнее изменение этой страницы: 2021-12-15; просмотров: 90; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.143.168.172 (0.102 с.)