flotor F11 (int Z1)
int * x;
int * y;
int * z;
x = int int;
y = int int;
z = int int new;
...
unde x, y, z sunt indicii pentru locațiile de memorie, de fiecare dată alocate funcțiilor pentru stocarea numerelor întregi acolo. Aceste numere sunt necesare numai în procesul funcției și, prin urmare, sunt date intermediare. La sfârșitul acestei funcții, trebuie să ștergem memoria alocată folosind comenzi cum ar fi:
șterge x;
ștergeți y;
șterge z;
Acest cod are dezavantajele descrise mai sus - re-crearea și distrugerea funcției date intermediare în funcție de fiecare dată când se numește o nouă funcție. Soluția la problemă ar putea fi următoarea: am putea transmite variabile intermediare funcției, ca argumente. Aceasta este, redesign codul după cum urmează:
float F11 (int Z1, int * x, int * y, int * z) <
...
Totul! Deoarece * x, * y, * z sunt acum argumente ale funcției, acestea trebuie să fie create o dată înainte de începerea funcției, ca orice alte argumente care sunt transmise la intrarea funcției. Astfel, în interiorul funcției F11 (), acum aceste variabile nu vor fi create și șterse de fiecare dată. Astfel, vom rezerva memoria pentru această funcție înainte de a o crea o dată, odată ce a creat variabilele corespunzătoare. În plus, este recomandabil să se dezvolte această abordare. În exemplul nostru, funcția noastră returnează un număr de tip float. Aceasta înseamnă că de fiecare dată în interiorul funcției este creată o variabilă de tip float, unde este scris rezultatul acestei funcții, iar această variabilă este distrusă după terminarea funcției. Aceasta este, de fapt, în corpul funcției:
float F11 (int Z1, int * x, int * y, int * z)
Din nou, avem acțiuni inutile de creare și distrugere. Aici vom crea o variabilă vix a formularului float în interiorul funcției, valoarea căreia funcția revine și variabila vix însuși distruge - curăță memoria care i-a fost alocată în cadrul funcției. Astfel, dacă funcția este executată de mai multe ori, atunci de mai multe ori, ca o chestiune de fapt, vom crea și distruge variabila vix gol. Astfel, luând în considerare cele de mai sus, este recomandabil să se facă acest lucru:
void F11 (int Z1, int * x, int * y, int * z, float * vix) <
...
>
Ca rezultat, în interiorul funcției noastre nu se creează variabile intermediare, iar funcția nu întoarce nimic. Tot ceea ce este necesar pentru munca sa aici este transmis funcției ca argumente. Ca urmare, de fiecare dată când funcția este pornită, nu este nevoie să alocați și să curățați memoria de fiecare dată. Toată această memorie, de fapt, este alocată o dată înainte de prima lansare a funcției. Funcția modifică numai datele din această memorie - în argumentele care sunt transmise la intrare. Memoria funcțiilor nu se alocă și nu se purifică. Astfel, am separat procesul de alocare a memoriei și procesul de lucru cu datele (memoria). Funcția funcționează acum doar cu datele din memorie, dar nu răspunde la alocarea și purjarea. Toate acestea sunt bine, dar, în primul rând, variabilele cu care funcționează funcția sunt argumentele ei, ceea ce înseamnă că ele nu sunt create în interiorul funcției - ele sunt externe funcției. Și dacă este așa, atunci ele pot fi schimbate nu numai de funcția însăși, ci și de altcineva în afară de această funcție. În timp ce variabilele create în cadrul unei funcții sunt disponibile numai pentru funcția în sine și nimeni altcineva. Și, în al doilea rând, dacă avem nevoie de un număr foarte mare de variabile intermediare, matrice etc. pentru funcția? La urma urmei, deseori o funcție este o subrutină destul de mare. Apoi, nu este convenabil să scrieți toate variabilele intermediare sub forma unor argumente ale funcției. Cel puțin ar fi bine să combinați toate aceste variabile într-o singură structură și să trimiteți o referință la această structură la intrarea funcției. Ca rezultat, dezvoltatorii de limbi au făcut ceva similar, dar în același timp au mers mai departe - au creat o structură specială, pe care o numeau o clasă. O clasă este o formă de organizare a codului unde, sub același nume (identificator), există atât funcția, cât și datele pe care este procesată. Asta este, funcția a fost plasată într-o singură structură împreună cu datele pe care această funcție ar trebui să le gestioneze și care nu trebuie acum să fie create în interiorul acestei funcții de fiecare dată când începe. Noi în clasă vom descrie în prealabil toate variabilele, tablourile, etc. - Toată memoria de care avem nevoie și, de asemenea, în clasă vom descrie funcțiile care vor funcționa cu aceste variabile (cu această memorie). Și chiar dacă funcțiile nu au argumente de intrare, atunci faptul că variabilele, matricele și așa mai departe. și funcțiile aparțin aceleiași structuri (o clasă) - aceasta spune computerului că funcțiile acestei clase pot funcționa cu variabile, tablouri și așa mai departe. această clasă, ca și propriile lor argumente. Mai mult, datele sunt împărțite în clase private și publice. Dacă descrieți, de exemplu, o variabilă ca fiind privată, atunci numai funcțiile acestei clase pot funcționa cu aceasta - și nimic altceva în afara acestei clase nu poate schimba această variabilă. Adică, datele de tip privat sunt, de fapt, variabile interne ale funcțiilor - numai funcția însăși le poate adresa și nimic altceva! Ei bine și datele de tip public sunt accesibile și că cine nu este membru al clasei date - adică În aceste date, în esență, scriem rezultatul funcției care este deja interesantă în afara acestei clase - adică aceasta este funcția pe care o vom reîntoarce dacă nu am folosi conceptul de clasă și nu ar funcționa decât cu funcții. Deci, o astfel de combinație de date și funcții pentru prelucrarea lor într-o singură structură (numită clasă) este esența unui astfel de concept ca încapsulare. După cum am aflat deja - practica a determinat această "mișcare" dezvoltatorilor de limbi de programare. Sa dovedit că acest lucru este optim când funcțiile efectuează numai seturi de operații asupra datelor. și descrierile de date și datele (memoria) pentru aceste funcții se află într-un loc separat de funcții. În același timp, funcțiile și datele de memorie pentru ele formează o singură construcție - o clasă. Un obiect, apropo, este o implementare concretă a unei clase. La fel ca într-o echipă ca:
int este o notație generică de tip generic și i este o variabilă tip intreg specifică, care este pentru că cele de mai sus este un integer int. Deci, cu clasele. O clasă este o desemnare generală a unui anumit tip de construcție, un obiect fiind o manifestare concretă a acestei construcții. Pentru tipuri, vezi și aici. Apropo, aici este de asemenea important ca următorul moment să fie legat doar de clase și tipuri. Adesea, atunci când descriem tipurile de variabile din program, de exemplu, apar următoarele:
Aceasta alocă în memorie locul potrivit pentru variabilele i, j, k, unde putem scrie date întregului tip. Cu toate acestea, dacă avem, de exemplu, clasa K1, în care există, de exemplu, variabilele a1, b1, c1 de tip int și funcția de prelucrare a acestor variabile funk1 ():
clasa K1 <
publice:
int a1, b1, c1;
void funk1 ();
apoi o descriere ca aceasta:
va duce la crearea obiectelor X, Y, Z în care memoria va fi alocată variabilelor lor a1, b1, c1, unde putem scrie datele corespunzătoare. Deci, acum putem scrie niște numere întregi în locațiile de memorie cu numele X.a1, X.b1, X.c1, Y.a1, Y.b1 și așa mai departe. a demonstrat că spațiul de memorie alocat în cadrul corespunzătoare 9 numere: 3 numere (a1, b1, c1) pentru obiectul X, 3 al obiectului Y, 3 al obiectului Z - totul este în situația cu anunțurile de tip int care este alocată memorie sub variabilele corespunzătoare. Dar va fi alocat un loc în memorie pentru fiecare funcție funk1 () a fiecărui obiect? Memoria va fi alocată sub X.funk1 (), Y.funk1 (), Z.funk1 ()? Se pare că nu există și acest lucru este logic, pentru că în caz contrar, am fi alocat memorie de trei ori sub aceeași funcție (pentru funcții care funcționează în același fel)! Și asta este o risipă proastă de resurse informatice. Astfel, în memoria c ++ funcțiile pentru fiecare dintre obiectele din aceeași clasă nu sunt alocate - funcțiile nu sunt replicate la crearea obiectelor de clasă. Adică, atunci când introducem conceptul de clasă, nu combinăm pur și simplu datele și funcțiile într-o singură structură (clasă), dar și acest concept implică refuzul de a duplica aceeași funcție în anumite obiecte din aceeași clasă.
Următorul concept al POR este moștenirea. Din nou, un exemplu de procesare a imaginilor. Lăsați-ne pe cineva să creeze o clasă pentru a lucra cu imagini. Suntem complet mulțumiți de această clasă, dar ne-ar plăcea, în plus față de posibilitățile de prelucrare a imaginilor pe care le oferă această clasă, să aibă funcții suplimentare. Procedura de moștenire ne permite să ne creăm propria clasă pentru a lucra cu imagini bazate pe o clasă existentă - clasa noastră va moșteni toate funcțiile celei anterioare și vom adăuga funcțiile de care avem nevoie. Asta e tot. Acest lucru este foarte convenabil, deoarece nu este nevoie să repetăm lucrarea privind dezvoltarea funcțiilor care există deja în clasa deja creată în fața noastră. Pur și simplu moștenim aceste funcții creând o clasă de moștenire a unei clase deja existente. Și în programul nostru, putem folosi atât clasa strămoș și clasa derivată, și în ciuda faptului că avem două foarte asemănătoare cu clase reciproc într-o clasă derivată, noi nu re-crea o funcție care există deja în clasele strămoș. Astfel, nu trebuie să rescriim aceste funcții în clasa succesorilor - pur și simplu folosim funcțiile clasei strămoșilor și, în final, nu reutilizăm memoria pentru aceleași funcții în diferite clase.
Și, în cele din urmă, polimorfismul. Aici totul este simplu și acest concept este, de fapt, dezvoltarea unui astfel de fenomen ca supraîncărcarea funcțiilor (anterior, preîncărcarea a fost numită redefinire). Aceasta este atunci când aceeași funcție poate funcționa diferit în funcție de tipul de date pe care l-ați trimis la intrare. De exemplu, aveți o anumită funcție de multiplicare, și atunci când îl treci ca un argument numere întregi, pur și simplu le multiplică, iar când argumentele sunt dându-i o matrice, atunci această funcție multiplică matricea conform regulii de multiplicare matrice. Adică, există o singură funcție, aceeași interfață (modul de interacțiune, mapare) este concepută pentru a multiplica date de diferite tipuri. Pentru a organiza codul în așa fel încât aceeași interfață să fie utilizată pentru a lucra cu date de diferite tipuri, există comenzi speciale de limbaj. Un alt exemplu este prelucrarea imaginilor. Acesta este momentul în care același program poate procesa în mod egal imagini cu diferite formate - bmp, jpeg, gif etc. în timp ce nu vă interesează formatul acestei imagini - trebuie să efectuați aceleași operații în program, indiferent de ce este acum formatul acestei imagini. Ca rezultat, polimorfismul vă permite să creați interfețe de program destul de convenabile.
Astfel, designul OOP nu este ceva inventat de cineva într-un mod arbitrar. Metodele OOP au provenit din practica în sine - acestea sunt metodele de lucru mai optimal doar cu informație și totul.
P.S. Pentru cei interesați în dispozitivul de calculator poate fi recomandabil aici această carte și, în special, capul de ea - „Ce este calculatorul“ (cartea în sine sau a unor capitole ale acesteia, puteți cumpăra aici).