wyklad2 cechy konstruktora i hermetyzacja

background image

Wykład 3

Konstruktor – cechy

główne

background image

Co robi konstruktor?

Wywołanie konstruktora powoduje wykonanie następujących zadań:

• obliczenie rozmiaru obiektu

• alokacja obiektu w pamięci

• wyczyszczenie (zerowanie) obszaru pamięci zarezerwowanej dla

obiektu (tylko w niektórych językach)

• wpisanie do obiektu informacji łączącej go z odpowiadającą mu

klasą (połączenie z metodami klasy) poprzez wskaźnik this

• wykonanie kodu klasy bazowej (w niektórych językach nie

wymagane)

• wykonanie kodu wywołanego konstruktora
Z wyjątkiem ostatniego punktu powyższe zadania są wykonywane

wewnętrznie i są wszyte w kompilator lub interpreter języka, lub

w niektórych językach stanowią kod klasy bazowej.

W językach programowania w różny sposób oznacza się konstruktor:

• w C++, PHP4, Javie i in. - jest to metoda o nazwie zgodnej z

nazwą klasy

• w Pascalu - metoda której nazwę poprzedzono słowem kluczowym

constructor.

• w PHP 5 - metoda o nazwie __construct

background image

Uwagi ogólne do konstruktorów:

1. Konstruktor

NIE MUSI

wystąpić w opisie klasy, czyli

obiekty nie muszą być jawnie wprowadzane
konstruktorem.

2. Nazwa konstruktora może być przeładowana

, czyli

stosowana wielokrotnie w opisie klasy z różnymi
listami argumentów. Wtedy kompilator odróżnia
konstruktory po listach argumentów, tak, jak w
przypadku

przeładowanych

nazw

funkcji.

Konstruktorów może więc być wiele.

3. Konstruktor

może

być

wywoływany

(a

nie

deklarowany!!) bez żadnych argumentów

. Jest to tak

zwany konstruktor domniemany. Czasem nazywamy
go domyślnym albo standardowym. Ze względu na
istotę przeładowania nazwy konstruktor domniemany
czyli bezargumentowy może wystąpić tylko raz. Jeśli
nie deklarujemy w klasie żadnego konstruktora, to
kompilator sam ustanawia właśnie konstruktor
domniemany do obsługi obiektów w programie. Każdy
konstruktor z argumentami, którym nadamy wartości
domyślne

czyli

niedefiniowalne

jest

także

konstruktorem domniemanym.

background image

Konstruktor jest zwykle deklarowany jako publiczny,
bo przecież wprowadzane nim obiekty mogą być
używane przez klasy zewnętrzne, a ponadto jest
funkcją, która MUSI być dostępna dla składników
klasy. Możemy jednak dla konstruktora przewidzieć
ochronę tak, jak dla klas za pomocą etykiet private
lub protected. Wówczas jednak także
konstruowane obiekty będą dostępne tylko w
obrębie klasy z tym konstruktorem jako private albo
jako protected tylko w zakresie klas dziedziczących.

Konstruktor może zamiast definiować obiekty
podawać kopie obiektów zawartych w innej klasie.
Wtedy jest to tak zwany

konstruktor kopiujący

.

Konstruktor może dokonywać konwersji typu obiekty
z jednego w drugi. Nazywamy go wtedy

konstruktorem konwertującym

.

background image

Konstruktor kopiujący

Przyjrzyjmy się wywołaniu konstruktora klasy o nazwie klasa:
 
klasa::klasa(klasa&)
 
Jego argumentem jest referencja do obiektu danej klasy. Czyli do

elementu, który w chwili uruchomienia tego konstruktora już
istnieje. Taki konstruktor nie konstruuje obiektu tylko tworzy kopię
innego, który już istnieje wśród obiektów klasy. Pozostałe argumenty
konstruktora są domniemane. Przykładami konstruktora kopiującego
mogą być:

X::X(X&)
 
lub
 
X::X(X&, float=3.1415, int=0)
 

background image

Konstruktor kopiujący c.d.

• Taki konstruktor wprowadza obiekty identyczne z już

istniejącymi, czyli ich kopie.

• Taki konstruktor może być wywołany przez program

niejawnie:

1.W sytuacji gdy do funkcji jest

przez wartość

przesyłany

obiekt klasy X. Wówczas tworzona jest kopia tego obiektu.

Jest to tzw. kopiowanie płytkie.

2.W sytuacji kiedy funkcja zwraca przez wartość obiekt klasy

X. Wtedy także tworzona jest kopia obiektu. To także jest

kopiowanie płytkie.

To, że konstruktor kopiujący podaje obiekt kopiowany przez

referencję daje mu możliwość

zmiany zawartości obiektu

klasy!!

(patrz przesyłanie argumentu do funkcji przez

wartość)

background image

Konstruktor kopiujący c.d.

• Nie można pominąć referencji w konstruktorze

kopiującym, bo gdyby konstruktor X wywoływał

obiekty swojej klasy X przez wartość, czyli

wytwarzałby swoją kopię, to powstaje nie zamknięta

pętla tworzenia kopii.

• Konstruktor z przyczyn logiki języka otrzymuje więc

warunki do tego aby uszkodzić oryginał!!

• Zabezpieczamy się przed taką sytuacją następująco:

X::X(const X&obiekt)

• Teraz konstruktor X wie, że obiekt klasy X musi być

wywoływany jako stały. Konstruktor kopiujący jest

domyślnie obdarzony moderatorem const, czyli nie

może zmienić sam siebie.

background image

Kopiowanie płytkie i głębokie

Wyróżniamy dwa typy kopiowania obiektów

zawierających pola będące wskaźnikami

• Kopiowanie płytkie
a. Kopiowanie wszystkich składowych (w tym

wskaźników)

b. Kopiowane są wskaźniki, a nie to, na co wskazują
• Kopiowanie głębokie
a. Alokacja nowej pamięci dla wskaźników
b. Kopiowanie zawartości wskazywanej przez

wskaźniki w nowe miejsce

c. Kopiowanie pozostałych pól, nie będących

wskaźnikami

background image

Głębokie kopiowanie

Kiedy obiekt zawiera wskaźnik do dynamicznie zaalokowanego

obszaru, należy zdefiniować operator przypisania

wykonujący głębokie kopiowanie

W rozważanej klasie należy zdefiniować operator

przypisania:
AType& AType::operator=(const AType& otherObj)

Operator przypisania powinien uwzględnić przypadki

szczególne:

1.

Sprawdzić przypisanie obiektu do samego siebie, np. A=A:

2.

if (this == &otherObj) // if true, do nothing

3.

Skasować zawartośc obiektu docelowego

4.

delete this->...

5.

Zaalokować pamięć dla kopiowanych wartości

6.

Przepisać kopiowane wartości

7.

Zwrócic *this

background image

Konstruktor kopiujący a operator

przypisania

Konstruktor kopiujący jest więc używany do

stworzenia nowego obiektu

• Wydaje się prostszy od operatora przypisania -

nie musi sprawdzać przypisania do samego siebie

i zwalniać poprzedniej zawartości

• Jest użyty do skopiowania parametru aktualnego

do parametru formalnego przy przekazywaniu

parametru przez wartość

• Przy tworzeniu nowego obiektu, można go

zainicjalizować istniejącym obiektem danego

typu. Wywołany jest wówczas konstruktor

kopiujący.

background image

Konstruktor kopiujący a operator

przypisania c.d.

int main() {
list a;
//...
list b(a); //copy constructor is called
list c=a; //copy constructor is called
};

background image

 
 
1.#include<iostream>
2.#include<string.h>
3.#include<conio.h>
4.class X
5.{public:char*p; X(char*);
6.};
7.class Y
8.{public:
9.char*p; Y(char*);
10.

Y(Y&);

// deklaracja konstruktora kopiajacego obiekty klasy Y

11.};
12.void main()
13.{
14.X x("xxx"); X j=x;

//powolanie do zycia obiektow x,j klasy X

15.cout<<"\nx="<<x.p<<", j="<<j.p; // wydruk wskaznika czyli adresu do

obiektow x,j

16.strcpy(j.p,"111"); // skopiowanie pod wskaznik obiektu j lancucha 111
17.cout<<"\nx="<<x.p<<", j="<<j.p;

Przykład: konstruktor kopiujący będzie kopiował wskaźnik do

obiektu. ( czy to tzw. kopiowanie głębokie ?)

background image

18.cprintf("\n\rx.p=%p, j.p=%p,x.p,j.p);
19.Y y("yyy"); Y d=y; //

powołanie obiektów klasy Y

20.cout<<"\ny="<<y.p<<", d="<<d.p;
21.strcpy(y.p,"222");
22.cout<<"\ny="<<y.p<<", d="<<d.p;
23.cprintf("\n\ry.p=%p, d.p=%p,y.p,d.p);
24.getch();
25.}
26.X::X(char*s)
27.{p=new char[80]; if(p)strcpy(p,s);
28.}
29.Y::Y(char*s)
30.{p=new char[80]; if(p)strcpy(this->p,s);
31.}
32.Y::Y(Y&y)
33.{p=new char[80]; if(p)strcpy(p,y.p);
34.}

background image

Omówienie przykładu:

5.{public:char*p; X(char*);

Wiersz 5: etykieta public dla klasy X oraz deklaracje
zmiennej własnej p, która jest wskaźnikiem do zmiennej
znakowej oraz konstruktor obiektów klasy X oczekującego
na liście parametrów formalnych wskaźnika do zmiennej
typu string lub charakter. Ciało tego konstruktora jest
podane w wierszu 26-28:

26.X::X(char*s)
27.{p=new char[80]; if(p)strcpy(p,s);
28.}

Wiersz 8-9: analogiczny jak wiersz 5 ale dla klasy Y

8.{public:
9.char*p; Y(char*);

background image

Wiersz 9: konstruktor kopiujący klasy Y. Będzie on
kopiował wskaźnik do zmiennej znakowej, którą
wskaże. Może to być zmienna z innej klasy czyli z
innego typu zmiennej obiektowej. Na tym polega
kopiowanie głębokie. W klasie X funkcjonuje konstruktor
kopiujący domyślny tworzony podczas kompilacji. Daje
on kopiowanie płytkie, czyli dotyczące tylko składników
własnej klasy X.

background image

13.{
14.X x("xxx"); X j=x; //powolanie do zycia obiektow

x,j klasy X

Wiersz 13-14: tworzymy obiekt x oraz obiekt j klasy X. Do obiektu x
wpisywany jest element tablicy zarezerwowanej dla niego przez
konstruktor w wierszu 26. Obiekt j jest inicjalizowany obiektem x.
Kopiowanie x do j jest realizowane przez konstruktor domyślny klasy
X. Przepisuje on wskaźnik do obiektu x do wskaźnika do obiektu j.
Dlatego wskaźnik p w obiekcie j będzie wskazywał to samo miejsce co
wskaźnik p w obiekcie x. Dlatego wydruk w wierszu 14 powinien
podać ten sam wynik dla każdego z tych obiektów.
Zauważmy, że obiekt j nie ma zarezerwowanej swojej przestrzeni na
tablice znakową, korzysta natomiast ze zmiennej wskaźnikowej
własnej p z klasy X do podłączenia się do tej samej tablicy co obiekt
x. Dlatego pojawia się szczególny zapis obiektów x oraz j połączony
ze zmienną własną wskaźnikową p.
15.cout<<"\nx="<<x.p<<", j="<<j.p; // wydruk wskaznika czyli
adresu do obiektow x,j
16.strcpy(j.p,"111"); // skopiowanie pod wskaznik obiektu j lancucha
111
17.cout<<"\nx="<<x.p<<", j="<<j.p;

background image

15.cout<<"\nx="<<x.p<<", j="<<j.p; // wydruk wskaznika
czyli adresu do obiektow x,j
16.strcpy(j.p,"111"); // skopiowanie pod wskaznik obiektu j
lancucha 111
17.cout<<"\nx="<<x.p<<", j="<<j.p;

Wiersz 15-17: do tablicy wskazywanej przez
wskaźnik p wpisujemy poprzez kopiowanie
łańcucha wartość ’’111” ale przedtem
sprawdzamy adresy obiektów.
Wiersz 16: wydruk wartości obiektu x oraz j
wskazywanych przez zmienną p
Wiersz 17: wydruk adresów wskazywanych przez p
dla obiektu x oraz j. Te adresy powinny być
jednakowe, czy nie?

background image

18.cprintf("\n\rx.p=%p, j.p=%p,x.p,j.p);
19.Y y("yyy"); Y d=y;

powołanie obiektów klasy Y

20.cout<<"\ny="<<y.p<<", d="<<d.p;
21.strcpy(y.p,"222");
22.cout<<"\ny="<<y.p<<", d="<<d.p;

Wiersze 18-22: powtórzenie takich samych działań ale dla
klasy Y. Wprowadzamy obiekty y oraz d, które grają takie
same role jak poprzednio x oraz j.
Wiersz 21: modyfikujemy łańcuch w obiekcie d.
Wiersz 22: drukujemy wartości obiektów y oraz d nie
spodziewając się ich identyczności jak poprzednio dla x
oraz j. Dlaczego? Dlatego, że konstruktor Y działa przez
referencję, a nie poprzez przypisanie jak konstruktor
kopiujący domyślny. Łańcuch d jest modyfikowany
tylko w miejscu d
. Konstruktor Y zapewnia modyfikację
poprzez referencję.
Wydruk adresów obiektów y oraz d. Powinny być różne!!

background image

26.X::X(char*s)
27.{p=new char[80]; if(p)strcpy(p,s);
28.}

Wiersz 26-28: ciało konstruktora obiektów klasy X.
Operatorem new jest dynamicznie przydzielona
pamięć dla tablicy 80cio znakowej. Kopiowanie
łańcucha z listy parametrów formalnych konstruktora
do tablicy nastąpi tylko wtedy, kiedy operator new
pamięć przydzieli.

background image

29.Y::Y(char*s)

30.{p=new char[80]; if(p)strcpy(this->p,s);

31.}

32.Y::Y(Y&y)

33.{p=new char[80]; if(p)strcpy(p,y.p);

34.}

Konstruktory klasy Y. Konstruktor kopiujący powiela
postać konstruktora poza wskazaniem, że dozwala
na kopiowanie obiektów klasy Y do wskaźnika p spod
adresy każdego obiekty klasy Y.

background image

Rezultat na ekranie (przykładowy):
 
x=xxx, j=xxx
x=111, j=111
x.p=2707:0004, j.p=2707:0004
y=yyy, d=yyy
y=yyy, d=222
y.p=270D:0004, d.p=2713:0004

background image

Jakie mamy więc metody

tworzenia obiektów?

Zmienne automatyczne

Atype a; //konstruktor domyślny
Zmienne automatyczne z argumentami

Atype a(3); //konstruktor z parametrem int
Przekazywanie parametrów funkcji przez wartość

void f(Atype b) {...} …..

Atype a; //konstruktor domyślny

f(a); //konstruktor kopiujący
Przypisanie wartości zmiennym

Atype a,b;…..

a=b; //operator przypisania
Inicjalizacja nowych obiektów

Atype b; //konstruktor domyslny

Atype a=b; //konstruktor kopiujący (NIE operator

przypisania)

Zwracanie wartości z funkcji

Atype f() {

Atype a; //konstruktor domyślny

return a; //konstruktor kopiujący

}

background image

Cechy (zalecane) poprawnie

napisanej klasy

Jawny konstruktor

• Gwarantuje, że każdy zadeklarowany egzemplarz obiektu zostanie

w kontrolowany sposób zainicjalizowany

Jeżeli obiekt zawiera wskaźniki do dynamicznie zaalokowanej

pamięci:

A. Jawny destruktor:

• Zapobiega wyciekom pamięci. Zwalnia zasoby podczas usuwania

obiektu.

B. Jawny operator przypisania

• Używany przy przypisywaniu nowej wartości do istniejącego

obiektu.

• Zapewnia, że obiekt jest istotnie kopią innego obiektu, a nie jego

aliasem (inną nazwą).

C. Jawny konstruktor kopiujący

• Używany podczas kopiowania obiektu przy przekazywaniu

parametrów, zwracaniu wartości i inicjalizacji. Zapewnia, że obiekt

jest istotnie kopią innego obiektu, a nie jego aliasem.

background image

Wykład 4

Przeciw pełnej

hermetyzacji

background image

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 etykiet nie ma, to w trybie
domyślnym wszystkie składniki klasy są private
. Są lokalne w
zakresie ważności 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
umieszczając etykietę przed nazwą składnika klasy. Wtedy private i
public mają sens podany wyżej. Dodatkowo stosuje się także etykietę
protected. Oznacza ona, że taki składnik jest dostępny tylko w
ramach dziedziczenia tj. w klasach, które są potomkami klasy
zawierającej ten składnik.
Tak więc etykiety właśnie zapewniają nam ochronę dostępu do
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. Etykiety te są jednym z narzędzi hermetyzacji klasy.

background image

Przykład:
class chamidlo
{
int a;
float b;
void fun1(int);
protected:
char m;
void fun2(void);

public:

int v;

void fun3(char*);
private:

int d;
void fun4(float b);

}
 
W tej klasie składniki prywatne, czyli dostępne tylko w obrębie

klasy chamidlo 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. Są to: v, fun3.

background image

Funkcje zaprzyjaźnione

 
To takie funkcje, które, mimo, że nie są składnikami klasy, to mają

dostęp do jej składników czyli innych funkcji, zmiennych i obiektów.
Mają dostęp także do tych składników klasy, które są hermetyzowane
etykietą private. Pamiętajmy, że jeśli nie ma innych etykiet, to
wszystkie składniki są private. Funkcja zaprzyjaźniona jest
wprowadzana instrukcją friend.

 

Sposób stosowania:
class figura{
 
int x,y;
…….
friend void goniec(figura&)
};

background image

Sama funkcja goniec(figura&) jest zdefiniowana gdzieś w
programie w całkowicie innym miejscu nie powiązanym z
klasą pionek. W klasie figura {} chcemy z niej skorzystać
nawet, jeśli przynależy ona do innej klasy. Wtedy poprawnie
jest taką funkcję zaznaczyć etykietą public w jej klasie.
 
Cechy funkcji zaprzyjaźnionych:
*Funkcja może być zaprzyjaźniona z kilkoma klasami.
*Na argumentach jej wywołania może wykonywać operacje
zgodnie ze swoją definicją.
*Może być napisana w zupełnie innym języku niż C++ i
dlatego może nie być funkcją składową klasy.

background image

*Ponieważ funkcja typu friend nie jest składnikiem klasy to nie ma
wskaźnika this, czyli musi się posłużyć operatorem wskaźnika, albo
przypisania aby wykonać działania (także te na składniku klasy, z
którą jest zaprzyjaźniona).
*Jest deklarowana w klasie ze słowem instrukcji friend i nie podlega
etykietom hermetyzcji (public, private, protected).
*Może być cała zdefiniowana w klasie i wtedy jest typu inline ale
nadal jest funkcją zaprzyjaźnioną.
*Nie musi być funkcją składową żadnej klasy ale może nią być.
*Klasa może się przyjaźnić z wieloma funkcjami, które są lub nie są
składnikami innych klas.
*Funkcje zaprzyjaźnione nie są przechodnie, to znaczy, że „przyjaciel
mego przyjaciela nie jest moim przyjacielem” czyli zaprzyjaźnienie
nie przenosi się od klasy do klasy.
*Zaprzyjaźnienie nie podlega mechanizmowi dziedziczenia.
*Z zasady umieszcza się funkcje zaprzyjaźnione na początku
wszystkich deklaracji w klasie.

background image

Zaprzyjaźnienie klas

#include <string>
using namespace std;
class Pies {

string kolor, przedmiot;

int wiek; friend class Kot;

public:

Pies(string, int, string); void drukuj();

void zamien(Kot *);
};
class Kot { string kolor, przedmiot; int wiek;
friend class Pies;
public:

Kot(string, int, string); void drukuj();

void zamien(Pies *);
void clear() { przedmiot=""; }
};

background image

Zaprzyjaźnienie klas cd.1

Pies::Pies(string aKolor, int aWiek, string aPrzedmiot)
{

• kolor = aKolor;

• wiek = aWiek;

• przedmiot = aPrzedmiot;
}

Kot::Kot(string aKolor, int aWiek, string aPrzedmiot)
{

• kolor = aKolor;

• wiek = aWiek;

• przedmiot = aPrzedmiot;
}

background image

Zaprzyjaźnienie klas cd.2

• void Pies::zamien(Kot *b)

• {

• string old = b->przedmiot;

• b->przedmiot = przedmiot;

• przedmiot = old;

• }

• void Kot::zamien(Pies *b)

• {

• string old = b->przedmiot;

• b->przedmiot = przedmiot;

• przedmiot = old;

• }

background image

Zaprzyjaźnienie klas cd.3

• void

• Pies::drukuj()

• {

• printf("Kolor [%s], wiek [%d], przedmiot [%s]\n",

kolor.c_str(), wiek,

• przedmiot.c_str());

• }

• void

• Kot::drukuj()

• {

• printf("Kolor [%s], wiek [%d], przedmiot [%s]\n",

kolor.c_str(), wiek,

• przedmiot.c_str());

• }

background image

Zaprzyjaźnienie klas cd.4

int main(int argc, char* argv[])
{

• Pies a("czarny", 3, "kosc"); Kot b("bialy", 5, "pilka");

• a.drukuj(); b.drukuj();

• printf("\nZamiana...\n\n"); a.zamien(&b); a.drukuj();

b.drukuj();

• printf("\nA zabiera B...\n\n"); a.zamien(&b); b.clear();

• a.drukuj(); b.drukuj();

• printf("\n"); system("PAUSE");

return 0;

}

background image

Cechy zaprzyjaźniania klas

• W klasie możemy deklarować przyjaźń z

funkcjami składowymi innej klasy bez ograniczeń.
Oznacza to, że obiekty klasy zaprzyjaźnionej i jej
składniki otrzymują dostęp do wszystkich – także
prywatnych – składników klasy zaprzyjaźnionej.

• Jedną klasę możemy zaprzyjaźnić z wieloma

innymi klasami.

• Przyjaźń nie jest przechodnia.
• Zaprzyjaźnianie klas jest wyłączone z procesu

dziedziczenia, czyli zaprzyjaźnienie nie jest
dziedziczone.


Document Outline


Wyszukiwarka

Podobne podstrony:
Sciaga ze stali-semV-wyklad4, BUDOWNICTWO, KONSTRUKCJE METALOWE 2
Cechy Konstrukcyjne masowców w porównaniu do kontenerowców
krawiec,podstawy konstrukcji maszyn I,cechy konstrukcyjne zasady konstrukcji
Notatki z wykładu Budownictwo Konstrukcje betonowe sem5
BUD OG wykład 10c Konstrukcje sprężone
1d Wykład CECHY RYNKOWE NIERUCHOMOŚCIid 18890 ppt
cechy konstrukcyjne nowoczesnych obrabiarek
ściąga zaliczenie wykładu, Uczelnia, Konstrukcje metalowe
WYKŁAD 06 KonstrJęzykowe
WYKŁAD I CECHY MOTORYCZNE, Studia, Fizjoterapia, Studia - fizjoterapia, Kształcenie Ruchowe
Psychologia osobowości dr Kofta wykład 8 Poznawcze konstruowanie rzeczywistości
BUD OG wykład 1 cechy materiałów
Cechy konstrukcji wsp+-+érz¦Ödnych, Filologia Polska, Gramatyka Opisowa
WYKLAD1, BUDOWNICTWO, KONSTRUKCJE METALOWE 2
Wykład 12 a, Konstrukcje Metalowe

więcej podobnych podstron