background image

 

 

Wykład 4

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) 

• 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 typu 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 

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


Document Outline