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 

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