wyklad3 abstrakcja zmiennych

background image

Obiekty jako zmienne w pamięci

dynamicznej komputera –

abstrahowanie zmiennych

wykład 3

background image

 

Składniki klasy-podsumowanie

• Składnikami klasy mogą, i najczęściej muszą być

deklarowane

zmienne,

funkcje,

nazywane

wówczas funkcjami składowymi, a także obiekty

innych klas i deklarowane konstruktory obiektów

własnych klasy. Składnikiem klasy będzie też

konstruktor

domniemany,

kopiujący

i

konwertujący występujące niejawnie na rzecz

obiektów klasy.

• Funkcje wchodzące w skład klasy w tym

znaczeniu, że wykonują czynności na rzecz

obiektów klasy mogą być definiowane wewnątrz

klasy albo na zewnątrz po bloku main() tak, jak

inne funkcje. Mogą funkcje składowe klasy być

także funkcjami biblioteki standardowej i wtedy

odpowiednie pliki nagłówkowe, zawierające te

funkcje, należy dołączyc preprocesorem. W

przykładach omawianych była to na przykład

funkcja strcpy() z pliku bibliotecznego string.h.

background image

Funkcja wykorzystywana dalej jako metoda
obiektów
może się znaleźć w pliku nagłówkowym
i nie spowoduje to kłopotów z linkowaniem
programu, kiedy będzie się ona wielokrotnie
wywoływana w programie. Tak samo zachowują
się funkcje z biblioteki standardowej. Jest to
możliwe dlatego, że taka funkcja składowa klasy
jest po kompilacji traktowana jako funkcja typu
inline, czyli wywoływana jednokrotnie. Każde jej
następne wywołanie to po prostu wykonanie ciała
funkcji bez konieczności poszukiwania jej adresu
przy każdym wywołaniu.

background image

Przykład:
Plik osoba.h
-------------------------------------------------------------------
#include<iostream.h>

#include<string.h>

class Osoba{
char nazwisko[80];
int wiek;
public:

void zapamietaj(char *napis, int lata);

void wypisz() {
cout<<”\t”<<nazwisko<<”,lat:”<<wiek<<endl;}
/// FUNKCJA wypisz JEST WEWNATRZ CIALA KLASY
};

background image

Zdefiniowany został plik nagłówkowy, który będzie włączany
wszędzie tam, gdzie w programie stosowana będzie klasa
Osoba.
Funkcja składowa klasy nazwana wypisz (...) wszędzie tam,
gdzie zostanie zastosowana będzie miała tylko formę:
 

cout<<”\t”<<nazwisko<<”,lat:”<<wiek<<endl;
 
Nie będzie każdorazowo wywoływana. Gdyby jednak funkcja
była

zdefiniowana poza ciałem klasy to nie może wystąpić w

pliku nagłówkowym

bo pojawią się błędy linkowania

(konsolidacji).
Jeśli funkcji składowych klasy jest wiele to warto przygotować
plik nagłówkowy, w którym są one definiowane wewnątrz
klasy.

background image

Funkcja i wskaźnik this

Rozpatrzmy funkcję klasy Osoba opisaną poniżej
 
void Osoba::zapamietaj(char *napis, int lata)

{

strcpy(nazwisko,napis);
wiek=lata;

}

Powołajmy do życia kilka obiektów klasy Osoba:
 
Osoba student1, student2, profesor, technik, inżynier
 
Mamy pięć obiektów i chcemy, aby do konkretnego wpisany

został wiek =23.

Wykonujemy to za pomocą podanej wyżej funkcji na przykład

następująco:

 
student2.zapamietaj(’’Tomek Gawin’’,23);

background image

Funkcja musi odszukać obiekt student2 aby wykonać operację na
jego rzecz. Robi to tak, że korzysta z ukrytego przed nami wskaźnika
this, który jest inicjalizowany w momencie pojawienia się operatora
kropki po nazwie obiektu. Ten wskaźnik pokazuje funkcji na którym
egzemplarzu (konkrecie, instancji) obiektów klasy ma wykonać
swoje czynności. Oznacza to, że postać funkcji należy rozumieć tak:
 
void Osoba::zapamietaj(char *napis, int lata)

{

strcpy(this->nazwisko, napis);
this->wiek=lata;

}

 Tablica nazwisko i zmienna wiek zostają „podczepione” pod
wskaźnik obiektu.
Zauważcie, że zmienna typu char jest w opisie funkcji wskaźnikiem
do zmiennej napis. To zapewni bezbłędne zadziałanie operacji this.
Wskaźnik this jest wstawiany we właściwe miejsce poza nasza
wiedzą. Jest to wskaźnik stały czyli typu
 

X const*

gdzie X to nasz obiekt.

background image

this - WSKAŹNIK SPECJALNY

• Każdej funkcji - metodzie zadeklarowanej

wewnątrz klasy zostaje w momencie

wywołania w niejawny sposób (ang.

implicitly) przekazany wskaźnik do obiektu (w

stosunku do którego funkcja ma zadziałać).

Pointer wskazuje funkcji w pamięci ten

obiekt, którego członkiem jest dana funkcja.

Bez istnienia takiego właśnie wskaźnika nie

moglibyśmy stosować spokojnie funkcji, nie

moglibyśmy odwoływać się do pola obiektu,

gdybyśmy nie wiedzieli

jednoznacznie, o który obiekt chodzi.

background image

this - WSKAŹNIK SPECJALNY, c.d.

• Program posługuje się automatycznie niejawnym

wskaźnikiem do obiektu (ang. implicit pointer). Możemy

wykorzystać ten istniejący, choć do tej pory nie widoczny dla

nas pointer posługując się słowem kluczowym this (ten). This

pointer wskazuje na obiekt, do którego należy funkcja.

Korzystając z tego wskaźnika funkcja może bez cienia

wątpliwości zidentyfikować właśnie ten obiekt, z którym

pracuje a nie obiekt przypadkowy.

[!!!] FUNKCJE KATEGORII static NIE OTRZYMUJĄ POINTERA

this.

Należy pamiętać, że wskaźnik this istnieje wyłącznie podczas

wykonywania metod (ang. class member function execution),

za

wyjątkiem funkcji statycznych.

background image

Obiekt jako zmienna – przesyłanie

obiektu do funkcji

Przesyłanie obiektu przez wartość

Przykład:
 
Plik prezentacja.c
 
///////
 
#include<iostream.h>
#include ”osoba.h”
///////
void prezentacja (Osoba);

//

nazwa klasy jest w tej deklaracji typem

argumentu funkcji

///////

background image

void main()
{
Osoba kompozytor, autor;

//

wywolanie obiektow klasy

Osoba do zycia

kompozytor.zapamietaj(”Aleksander Borodin”, 54);
autor.zapamietaj(”Alosza Jerofiejew”, 33);
///

teraz wywołamy obiekty poprzez wywołania funkcji, w której

sa one argumentami

 
//////
prezentacja(kompozytor);

//

argument podany jawnie czyli przez

wartosc

prezentacja(autor);
}
/////////
void prezentacja(Osoba ktos)

//

to jest opis ciala funkcji i tu

argument ma już nazwe poza typem

{
cout<<”\n przedstawiam panstwu , oto we wlasnej osobie:”;
ktos.wypisz();
}

background image

Co wytworzymy na ekranie poprzez wykonanie tego
programu?
 
przedstawiam panstwu, oto we wlasnej osobie
Aleksander Borodin, lat:54
przedstawiam panstwu, oto we wlasnej osobie Alosza
Jerofiejew, lat:33
 
 
 
Wystąpiło tu

stosowanie obiektu jako argumentu

funkcji.

Obiekt inicjalizował się jako kopia wewnątrz

funkcji. Przy obiektach o skomplikowanej budowie nie
jest to dobry sposób, bo tworzenie kopii obiektu
wewnątrz funkcji musi trwać odpowiednio długo.
Wypisanie na ekran nastąpiło za pomocą funkcji
dołączonej w pliku nagłówkowym osoba.h.

background image

Przesyłanie obiektu przez referencję

Przesyłanie przez referencję działa tak samo jak dla wszystkich
innych zmiennych w programie.

Referencja działa jak przezwisko

zmiennej

pokazujące, gdzie jej szukać. Nie jest tworzona kopia

zmiennej w funkcji, a tylko podawane miejsce jej ulokowania. W
podanym przykładzie wystarczy zmienić postać funkcji prezentacja.
 
Przykład:
 
Plik prezentacja.c
 
///////
 
#include<iostream.h>
#include <osoba.h>
///////

background image

void prezentacja (Osoba

&

);

///////
main()
{
Osoba kompozytor, autor;
kompozytor.zapamietaj(’’Aleksander Borodin”, 54);
autor.zapamietaj(’’Alosza Jerofiejew”, 33);
/// teraz wywołamy obiekty poprzez wywołania funkcji
 
//////
prezentacja(kompozytor);
prezentacja(autor);
}
/////////
 
void prezentacja(Osoba

&

ktos)

{
cout<<”\n przedstawiam panstwu ,oto we wlasnej osobie:”;
ktos.wypisz();
}
Czyli argument funkcji jest deklarowany przez referencję i obiekt i to
wystarcza aby funkcja pracowała na oryginale obiektu w miejscu jego
ulokowania na stosie RAM. Tworzenie kopii nie jest teraz konieczne.

background image

Operatory new i delete

Podczas deklarowania zmiennych rezerwowany jest dla ich obszar
pamięci. Jeśli się do zadeklarowanych zmiennych odwołamy, to
program musi wiedzieć, gdzie dokładnie szukać ich wartości w
pamięci operacyjnej.
Szczególne kłopoty powstają wówczas, kiedy deklarujemy zmienną,
która ma wiele elementów o różnych lokalizacjach w RAM. Taką
zmienną jest np. tablica. Jak pamiętamy, nie możemy się do niej
odwołać w programie jeśli w momencie odwołania nie jest znany
rozmiar tablicy.
Możemy zastosować odwołanie dynamiczne do pamięci za pomocą
operatora new. Wtedy wystarczy nazwa zmiennej, w tym tablicy i nie
musimy znać jej rozmiaru, a mimo to wszystkie elementy zostaną
poprawnie odszukane do dalszego zastosowania w programie.
Operator new musi działać nie na zmiennej tylko na wskaźniku do
zmiennej, czyli na wskazaniu miejsca (adresu) w pamięci
operacyjnej. Mówimy potocznie, że jest to miejsce na stercie (heap).

background image

Przykład: program tworzy listę osób z nazwiskami i imionami. Dane
o osobach umieszczane są w tablicach. W jednej umieszcza się
nazwiska, a w drugiej imiona. Do opisu posłuży obiekt, który ma
adres zwracany przez operator new.
 
#include <conio.h>
#include <stdlib.h>
#include <string.h>
 
class Osoba {
public:
char imie[80], nazw[80];
};

background image

void main()
{
//void clrscr();
Osoba *p1=new Osoba, *p2=new Osoba;
if(!p1||!p2)
{
cprintf(“\n\rp1 i/lub p2?”);
getch();
exit(0);
}
strcpy(p1->imię,”Jan”);
strcpy(p1->nazw,”Krol”);
*p2= *p1;
strcpy(p1->imię,”Ewa Nowak-”);
cprintf( ”\n\r%s%s”,p1->imie, p1->nazw);
cprintf( ”\n\r%s%s”,p2->imie, p2->nazw);
delete p1;delete p2;
getch();
}

Sposób
użycia
operatora
new do
obiektów

background image

Co zobaczymy na ekranie?
 

Jan Krol
Ewa Nowak-Krol

 
Jak to działa?
Pliki nagłówkowe: conio.h zawierają biblioteki do funkcji cprintf();
string.h zawiera biblioteczna funkcje strcpy();.

#include <conio.h>

#include <stdlib.h>
#include <string.h>
Niektóre pliki nagłówkowe nie musza być pisane z
rozszerzeniem h.

background image

Klasa Osoba nie zawiera konstruktora. Po kompilacji mamy więc
konstruktor Osoba domniemany. Dostępne są dla każdego obiektu
tego konstruktora zmienne tablicowe: imie[], nazw[].

class Osoba {
public:
char imie[80], nazw[80];
};

background image

Obiekty p1 i p2 jako konkrety klasy Osoba zostają przywołane
poprzez wskaźniki o adresach zwracanych operatorem new. Ten
operator przydziela jednocześnie wskaźnikom *p1 oraz *p2 miejsca
w pamięci. Może się zdarzyć, że w RAM nie będzie miejsca na
przydzielenie pamięci dla p1 oraz p2. Wtedy zostanie im nadana
przez operator new wartość NULL. Gdyby tak się zdarzyło, to
zostanie to wykryte przez if(!p1||!p2), a potem wydrukowane w
formie wartości p1 oraz p2 na ekranie w kolejnym wierszu
programu. Następnie wykonana by była instrukcja exit(0), co
oznacza zakończenie programu poprze wyjście poza ostatnia linię
kodu. Funkcja exit() jest umieszczona w pliku stdlib.h.

Osoba *p1=new Osoba, *p2=new Osoba;

if(!p1||!p2)

{

cprintf(“\n\rp1 i/lub p2?”);

getch();

exit(0);

}

background image

Funkcja strcpy() z biblioteki nagłówkowej string.h kopiuje
łańcuchy Jan oraz Krol do tablic imie[] i nazw[]. Tablice są w
obiekcie wskazywanym przez p1. Jeśli obiekt jest wskazywany przez
wskaźnik, to odwołujemy się do niego operatorem strzałki, a nie
kropki jak w poprzednich przykładach.
Pod adres wskaźnika p2 zostaje wpisana deferencja czyli zawartość
spod adresu *p1.

strcpy(p1->imię,”Jan”);
strcpy(p1->nazw,”Krol”);
*p1= *p2;

background image

Do tablicy imie[] wskazywanej przez p2 kopiowany jest łańcuch Ewa
Nowak-.
Drukowane są tablice z obiektów p1 oraz p2.
Operator delete usuwa obiekty p1 oraz p2 ze sterty pamięci
dynamicznej i tym samym zwalnia pamięć.

strcpy(p1->imię,”Ewa Nowak-”);
cprintf( ’’\n\r%s%s”, p1->imie,p1->nazw);
cprintf( ’’\n\r%s%s”, p2->imie,p2->nazw);
delete p1;delete p2;

getch();
}

background image

Wskaźniki do obiektów

• Wskaźniki do obiektów funkcjonują podobnie jak wskaźniki

do

struktur. Operator -> pozwala na dostęp zarówno do danych

jak i

do funkcji. Dla przykładu wykorzystamy obiekt naszej

prywatnej

klasy Licznik.

class Licznik

{

public:

char moja_litera;

int ile;

Licznik(char znak) { moja_litera = z; ile = 0; }

void Skok_licznika(void) { ile++; }

};

background image

Wskaźniki do obiektów, c.d.

• Aby w programie można było odwołać się do obiektu nie

poprzez

nazwę a przy pomocy wskaźnika, zadeklarujemy wskaźnik

do

obiektów klasy Licznik:

Licznik *p;

Wskaźnik w programie możemy zastosować np. tak:

p->Skok_licznika();

(czytaj: Wywołaj metodę "Skok_licznika()" w stosunku do

obiektu

wskazywanego w danym momencie przez wskaźnik p)

background image

Wskaźniki do obiektów, c.d.

• Trzeba pamiętać, że sama deklaracja w

przypadku referencji i wskaźników nie

wystarcza. Przed użyciem należy jeszcze

zainicjować wskaźnik w taki sposób, by

wskazywał na nasz obiekt-licznik.

Wskaźnik do obiektu inicjujemy w taki sam

sposób jak każdy inny pointer:

p = &Obiekt;


Document Outline


Wyszukiwarka

Podobne podstrony:
wyklad 5 prady zmienne
Wyklad6 wspolzaleznosc zmiennych
13 Wykład XIII Zmienność DNA pozajądrowego
Wykład współzależność zmiennych
Wykład6 współzależność zmiennych
Wykład5, Dobór zmiennych do modelu - Hellwig, Dobór zmiennych do modelu
13 Wykład XIII Zmienność DNA pozajądrowego
wyklady abstract 78
Wykład Szacowanie zmienności
Wyklad 2 zmiennosc standaryzacja 5 III 2014 b
wyklad 4 zmiennosc
M.Walczak - wyklad 4 - rachunek kosztów zmiennych a rachunek kosztów pełnych, Zarządzanie, rachunkow
wykład, RACHUNEK ROZNICZKOWY FUNKCJI JEDNEJ ZMIENNEJ 63, 1)
M.Walczak - wyklad 5 - rachunek kosztów zmiennych a rachunek kosztów pełnych ciąg dalszy, Zarządzani
STATYSTYKA WYKŁAD wybrane rozkłady zmiennych lsoowych
Blazek wyklady z ub roku (2006-07), Zmienne konatywne, Zmienne konatywne- projekty, dążenia, zadania
wyklad IIIa z RZ BZ MSU 2009 rach kosztów zmiennych a zarządzanie kosztami

więcej podobnych podstron