w3, Programowanie obiektowe


wykład 3

Obiekty jako zmienne w pamięci dynamicznej komputera

Składniki klasy-podsumowanie

Składnikami klasy mogą, i najczęściej muszą być deklaracje zmiennych, funkcje, nazywane wówczas funkcjami składowymi, a także obiekty innych klas i deklaracje konstruktorów obiektów własnych 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, na zewnątrz po bloku main() tak, jak normalne inne funkcje. Mogą też być funkcjami biblioteki standardowej i wtedy odpowiednie pliki nagłówkowe należy wywołać preprocesorem. W przykładach omawianych była to na przykład funkcja strcpy() z pliku bibliotecznego string.h.

Funkcja może się znaleźć w pliku nagłówkowym i nie spowoduje to kłopotów z linkowaniem programu, kiedy będzie się ona wielokrotnie pojawiać w programie. Jest to możliwe dlatego, że 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.

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;}

};

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 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 pojawia się błędy linkowania.

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

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 tej klasy:

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 następująco:

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

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 funkcja na którym egzemplarzu 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);

tis->wiek=lata;

}

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.

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);

///////

main()

{

osoba kompozytor, autor;

komposytor.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<<”przedstawiam panstwu \n” „oto we wlasnej osobie:”;

ktos.wypisz();

}

Co na ekranie z tego programu?

Mam zaszczyt przedstawić państwu, oto we własnej osobie Aleksander Borodin, lat:54

Mam zaszczyt przedstawić państwu, oto we własnej osobie Alosza Jerofiejew, lat:33

Wystąpiło tu stosowanie obiektu jako argumentu funkcji zadanego przez podstawienie z pliku nagłówkowego osoba.h. 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.

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”

///////

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<<”przedstawiam panstwu \n” „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.

Ukrywanie informacji - etykiety private i public

Jeśli klasa ma etykietę private, to jej składniki będą dostępne tylko w zakresie wnętrza klasy. Jeśli etykieta jest public, to składniki klasy mogą być wywoływane także spoza tej klasy. Etykietami możemy określać także dostęp do wybranych składników klasy wybiórczo. Wtedy private i public mają sens podany wyżej. Dodatkowo stosuje się wówczas etykietę protected. Oznacza ona, ze taki składnik jest dostępny tylko w ramach dziedziczenia tj. w klasach, które są potomkami klasy zawierającej ten składnik.

Jeśli etykiet nie ma, to w trybie domyślnym składniki klasy są private.

Tak wiec etykiety właśnie zapewniają nam ochronę składników klasy. Dane najczęściej umieszczamy w klasach chronionych czyli private. Do ustawiania wartości i pobierania danych korzystamy z funkcji składowych klasy. To do nich stosujemy etykiety ochrony.

Przykład:

class dzika

{

int a;

float b;

void fun1(int);

protected:

char m;

void fun2(void);

public:

int v;

void fun3(char*);

private:

int d;

fun4(b);

}

W tej klasie składniki prywatne, czyli dostępne tylko w obrębie klasy dzika to: a,b,fun1,d, fun4. Składniki protected, czyli zastrzeżone dla tej klasy i jej potomków to: m,fun2. Pozostałe składniki są publiczne, czyli dostępne dla wszystkich elementów programu.

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 zmienna, która ma wiele elementów o różnych lokalizacjach w RAM. Taką zmienna 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 taki 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).

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 <alloc.h>

#include <stdlib.h>

#include <string.h>

class Osoba {

public:

char imie[80], nazw[80];

};

void main()

{

clrscr();

cprintf(“\n\rmem=%lu', farecoreleft());

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

if(!p1||!p2)

{

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

getch();

exit(0);

}

cprintf(“\n\rmem=%lu', farecoreleft());

strcpy(p1->imię,”Jan”);

strcpy(p1->nazw,´´Krol”);

*p1= *p2;

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;

cprintf(“\n\rmem=%lu', farecoreleft());

getch();

}

Co zobaczymy na ekranie?

mem=532592

mem=5325240

Jan Krol

Ewa Nowak-Krol

mem532592

Jak to działa?

Pliki nagłówkowe: conio.h zawierają biblioteki do funkcji cprintf(); string.h zawiera biblioteczna funkcje strcpy(); alloc.h zawiera funkcję farecoreleft() a współpracuje z alloc.h z pliku stdlib.h, którego funkcja malloc() zwraca wskaźnik do najwyższego zajętego bloku pamięci operacyjnej.

Przykładowy opis w Helpie pliku stdlib.h jest jak poniżej:

Prototype in:

alloc.h stdlib.h

Syntax: void *malloc(size_t size);

size is in bytes. Returns a pointer to the

newly allocated block, or NULL if not enough

space exists for the new block. If size == 0,

it returns NULL.

Wniosek: odrębnym zagadnieniem do przestudiowania jest poznanie standardowych bibliotek wejścia/wyjścia oraz bibliotek operowania pamięcią.

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

Funkcja cprintf() wykona: przesunięcie do końca linii i przejście do początku następnej linii \n. Potem przesunie kursor do jej początku \r i zapisz mem=. Następnie skorzysta z funkcji farecoreleft(). Ta funkcja podaje w bajtach ilość pamięci od ostatniego zajętego bloku do końca RAMu. Używa przy tym liczby w formacie long integer bez znaku. Dlatego w wydruku rezerwujemy miejsce %lu.

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.

Następnie drukowany jest zapas pamięci w sposób podany wyżej. Ponieważ powstały obiekty p1 oraz p2, to rozmiar wolnej pamięci jest mniejszy o ich objętość.

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.

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ęć.

Ponownie drukowana jest wielkość wolnej pamięci i teraz jest taka jak przed utworzeniem obiektów.



Wyszukiwarka

Podobne podstrony:
Programowanie obiektowe(ćw) 1
Zadanie projekt przychodnia lekarska, Programowanie obiektowe
Programowanie obiektowe w PHP4 i PHP5 11 2005
Programowanie Obiektowe ZadTest Nieznany
Egzamin Programowanie Obiektowe Głowacki, Programowanie Obiektowe
Jezyk C Efektywne programowanie obiektowe cpefpo
Programowanie Obiektowe Ćwiczenia 5
Programowanie obiektowe(cw) 2 i Nieznany
programowanie obiektowe 05, c c++, c#
Intuicyjne podstawy programowania obiektowego0
Programowanie obiektowe, CPP program, 1
wyklad5.cpp, JAVA jest językiem programowania obiektowego
projekt01, wisisz, wydzial informatyki, studia zaoczne inzynierskie, programowanie obiektowe, projek
przeciazanie metod i operatorow, Programowanie obiektowe
projekt06, wisisz, wydzial informatyki, studia zaoczne inzynierskie, programowanie obiektowe, projek
projekt07, wisisz, wydzial informatyki, studia zaoczne inzynierskie, programowanie obiektowe, projek
Programowanie Obiektowe Cz2, Dziedziczenie proste
Programowanie obiektowe, w2, 2

więcej podobnych podstron