background image

 

 

Wykład 4

Konstruktory –cechy 

główne

background image

 

 

Uwagi ogólne do konstruktorów:

1. Konstruktor 

NIE MUSI

 wystąpić w opisie klasy, czyli 

obiekty nie muszą być 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. 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.  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

 

 

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

 

 

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

 

 

 
 
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. (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


Document Outline