©2006 Jerzy Kluczewski
PROGRAMOWANIE OBIEKTOWE
Ćwiczenia podstawowe Cz. II
•
Przestrzenie nazw
•
Obiekty
•
Klasy
•
Odwołania do składowych obiektu
•
Właściwości obiektów
•
Przeciążanie metod
•
Sposoby przekazywania argumentów
•
Dynamiczne obiekty
•
Konstruktory i destruktory
•
Konstruktor kopiujący
•
Składowe statyczne
•
Dziedziczenie i klasy potomne
•
Dostęp do składowych klasy
•
Modyfikatory i dziedziczenie
•
Przesłanianie składowych klasy
•
Prosta hierarchia klas
2
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II.
______________________________________________________________________
______________________________________________________________________
©2006 Jerzy Kluczewski
// MojeSortowanie.h
//---------------------------------------------------------------------------
// definicja przestrzeni nazw
namespace sudoku
{
#define SIZE 10
// STRUKTURA przechowująca cyfrę i jej częstosc
typedef struct {
int cyfra;
int czestosc;
} TCzestosc;
// TABLICA przechowująca indeksy o największej liczbie wystąpień
// cyfr w tablicy liczba_cyfr - posortowane rosnąco
static TCzestosc Cyferki[SIZE];
//---------------------------------------------------------------------------
// zeruj częstosc cyfr w tablicy cyferki
void zeruj_czestosc_cyfr(void)
{
for (int i=0; i<SIZE; i++)
{
Cyferki[i].cyfra = i;
Cyferki[i].czestosc = 0;
}
}
I.
Przestrzenie nazw
Przykładowy program składa się z dwóch plików: MojeSortowanie.h (biblioteka
obsługująca przestrzeń nazw sudoku) oraz MojeSortowanie.cpp (plik źródłowy
programu korzystający ze zmiennych i funkcji należących do przestrzeni nazw sudoku)
W pliku nagłówkowym w przestrzeni nazw sudoku, zdefiniowano następujące
elementy:
•
stałą SIZE
•
strukturę TCzestosc
•
tablicę statyczną Cyferki[ROZMIAR_TABL]
•
funkcję zeruj_czestosc_cyfr
•
funkcję Cyferki_Sortuj
Oto plik nagłówkowy MojeSortowanie.h
Plik MojeSortowanie.h.
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II. 3
______________________________________________________________________
©2006 Jerzy Kluczewski
//---------------------------------------------------------------------------
// znajdź indeksy na których pozycji jest największa
// liczba wystąpień cyfr z tablicy Cyferki i posortuj rosnąco
void Cyferki_Sortuj(void)
{
int nr_cyfry, maksimum;
TCzestosc temp;
// sortuj met. bąbelkową rosnąco
// TCzestosc temp służy jako zmienna tymczasowa
for (int i=0; i<SIZE; i++)
{
for (int j=SIZE-1; j>=i; j--)
if (Cyferki[j-1].czestosc > Cyferki[j].czestosc)
{
temp.cyfra = Cyferki[j-1].cyfra;
temp.czestosc = Cyferki[j-1].czestosc;
Cyferki[j-1].cyfra = Cyferki[j].cyfra;
Cyferki[j-1].czestosc = Cyferki[j].czestosc;
Cyferki[j].cyfra = temp.cyfra;
Cyferki[j].czestosc = temp.czestosc;
};
};
};
}; // koniec przestrzeni nazw
#endif
Obowiązujący obecnie standard C++ umożliwia właśnie tworzenie własnych
przestrzeni nazw. Aby to wykonać należy skorzystać z następującego wzorca:
Ciąg dalszy pliku MojeSortowanie.h.
Po takim zdefiniowaniu przestrzeni nazw, można w innym pliku używać jej
składowych; są dwa sposoby:
•
pierwszy polega na użyciu konstrukcji
nazwa_przestrzeni::nazwa_składowej
•
natomiast drugi polega na użyciu konstrukcji
using nazwa_przestrzeni;
nazwa_składowej
namespace nazwa_przestrzeni
{
// składowe przestrzeni nazw
};
4
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II.
______________________________________________________________________
______________________________________________________________________
©2006 Jerzy Kluczewski
#include <iostream>
#include "MojeSortowanie.h"
using namespace std;
int main()
{
int cyfra;
sudoku::zeruj_czestosc_cyfr();
for (int i=0; i<10; i++)
{
cout << "Podaj cyfre: ";
cin >> cyfra;
sudoku::Cyferki[cyfra].cyfra = cyfra;
sudoku::Cyferki[cyfra].czestosc++;
}
sudoku::Cyferki_Sortuj();
cout << "Cyfra: ";
for (int i=0; i<SIZE; i++) cout<<cout.width(2)<< sudoku::Cyferki[i].cyfra;
cout << endl;
cout << "Czestosc: ";
for (int i=0; i<SIZE; i++)
cout << cout.width(2) << sudoku::Cyferki[i].czestosc;
cout << endl << endl;
system("pause");
return 0;
}
W drugim przypadku można korzystać ze wszystkich elementów składowych
zdefiniowanych w danej przestrzeni nazw, tak jakby były one zdefiniowane w
aktualnym pliku.
Pierwszy sposób zastosowano w pliku MojeSortowanie.cpp.
Oto plik źródłowy MojeSortowanie.cpp
Program główny MojeSortowanie.cpp.
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II. 5
______________________________________________________________________
©2006 Jerzy Kluczewski
Przestrzeń nazw std
Często powstaje pytanie czy używać iostream czy iostream.h ? Współczesne
kompilatory C++ zawierają zazwyczaj dwie wersje biblioteki obsługującej strumienie
wejścia-wyjścia iostream: wersję starszą, której historia sięga początków języka C++,
oraz wersję nowszą, która powstała oficjalnie po standaryzacji języka w 1998 roku.
Teoretycznie pisząc #include <iostream.h> , korzystamy z wersji starszej, natomiast
pisząc #include <iostream> - z wersji nowszej. W praktyce zależy to od ustawień
kompilatora. W przypadku środowiska Borland C++ Builder , pisząc #include
<iostream.h>, domyślnie i tak korzystamy z nowej biblioteki.
Zapis ten ma takie samo znaczenie jak:
#include <iostream>
using namespace std;
W nowej wersji biblioteki iostream wszystkie składowe zostały przeniesione do
przestrzeni nazw std, a zatem aby z nich skorzystać, należy użyć powyższej dyrektywy
using albo też nazwę każdej składowej poprzedzić nazwą std oraz podwójnym
dwukropkiem, na przykład aby odwołać się do standardowego strumienia wyjściowego
należy użyć konstrukcji
std::cout.
6
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II.
______________________________________________________________________
______________________________________________________________________
©2006 Jerzy Kluczewski
II.
Obiekty
Czym są obiekty? Obiektem może być praktycznie wszystko (Rys. 1): dom, samochód,
osoba, atom, a w programowaniu obiektem jest każdy abstrakcyjny byt, który zapragnie
utworzyć programista w pamięci, np. macierz liczb całkowitych, albo stan pól jakiej gry
planszowej.
Obiekt może przechowywać dane, a także można na nim wykonywać różne operacje.
Obiekt może zawierać inne obiekty, tak samo jak wewnątrz samochodu znajdują się
fotele, kierownice, silnik.
Rys. 1 Obiekty
Projekt obiektu
W realnym świecie większość obiektów powstaje na podstawie projektu. W praktyce
programowania także występuje projektowanie obiektów. Najpierw powstaje projekt, a
potem są powoływane do życia (można powiedzieć tworzone) obiekty o budowie
i zachowaniu takim jakie przewiduje jego projekt. Taki projekt nazywamy klasą.
W programowaniu obiektowym – obiekty nie mogą istnieć bez swojego projektu,
natomiast projekt może istnieć bez obiektów.
Obiekt 1
Obiekt 2
Obiekt 3
Obiekt 4
Obiekt 5
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II. 7
______________________________________________________________________
©2006 Jerzy Kluczewski
III.
Klasa
Klasa to projekt (wzorzec) obiektów, a obiekt to konkretna realizacja (egzemplarz)
zdefiniowanej uprzednio klasy (Rys. 2).
Klasa to także typ definiowany przez programistę, dlatego w środowisku Borland C+
Builder przyjęto konwencję, że nazwy klas rozpoczynają się od litery T.
Rys. 2 Klasa i jej obiekty
Klasę deklaruje się za pomocą słowa kluczowego class.
Oto wzór deklaracji klasy:
Uwaga: Nie należy zapominać o średniku znajdującym się po zamykającym nawiasie
klamrowym.
Słowo public oznacza, że dostęp do wnętrza klasy jest publiczny, czyli że do
elementów klasy można się odwoływać bez ograniczeń.
Obiekt 1
Obiekt 2
Obiekt 3
Obiekt 4
Klasa (projekt obiektów) –
opisuje właściwości i sposoby
zachowania się obiektów
class nazwa_klasy
{
public:
// definicja wnętrza klasy
};
8
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II.
______________________________________________________________________
______________________________________________________________________
©2006 Jerzy Kluczewski
Klasa TPunkt
Oto deklaracja klasy TPunkt będąca projektem obiektów przechowujących dane
(współrzędne x i y punktów) o położeniu punktów na ekranie.
Zmienne x, y nazywa się składowymi klasy.
Pola klasy
Dane przechowywane w klasie (zmienne) nazywamy polami klasy.
Metody klasy
Kod umieszczany w klasie (funkcje działające na polach) nazywamy metodami klasy.
Klasa zawiera pola (dane) oraz metody (wykonywane czynności).
Deklaracje klasy (określające strukturę klasy), zazwyczaj umieszcza się w plikach
nagłówkowych z rozszerzeniem .h, natomiast definicje klasy w plikach .cpp.
Określenie struktury klasy
Plik Punkt.h
Do klasy TPunkt dodano dwie metody, które mają za zadanie pobierać wartości pól
klasy. Metody te noszą nazwy pobierzX i pobierzY.
class TPunkt
{
public:
int x;
int y;
};
class TPunkt
{
public:
int x;
int y;
int pobierzX();
int pobierzY();
};
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II. 9
______________________________________________________________________
©2006 Jerzy Kluczewski
Powyższa deklaracja klasy TPunkt oznacza:
Definiuję nowy typ danych TPunkt.
Obiekty tego typu będą zawierały pola o nazwach x i y, oba typu int.
Obiekty tego typu będą zawierały dwie metody – jedna o nazwie pobierzX, druga
będzie się nazywała pobierzY, obie będą zwracały wartość typu int.
Teraz należy zdefiniować, co będą robiły te metody. Definicje metod należy wpisać do
pliku Punkt.cpp.
Definicje te muszą mieć następującą postać:
Do pliku Punkt.cpp dopisano definicje dwóch metod: pobierzX, pobierzY, pierwsza z
nich odczytuje wartość pola x z obiektu klasy TPunkt, a druga odczytuje wartość pola
y z obiektu klasy Punkt.
Plik Punkt.cpp
Uwaga: Przy tworzeniu pliku głównego programu Punkt.cpp, nie można zapomnieć
o dołączeniu do niego pliku nagłówkowego zawierającego deklaracje klasy (Punkt.h).
Po utworzeniu plików Punkt.h i Punkt.cpp można już zająć się tworzeniem obiektów
klasy TPunkt.
Służy do tego następująca konstrukcja:
nazwa_klasy nazwa_obiektu;
typ_zwracany nazwa_klasy :: nazwa_metody()
{
// treść metody
};
#include "Punkt.h"
#include <iostream.h>
int TPunkt::pobierzX()
{
return x;
}
int TPunkt::pobierzY()
{
return y;
}
10
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II.
______________________________________________________________________
______________________________________________________________________
©2006 Jerzy Kluczewski
#include "Punkt.h"
#include <iostream.h>
int TPunkt::pobierzX()
{
return x;
}
int TPunkt::pobierzY()
{
return y;
}
int main()
{
TPunkt punkt;
punkt.x = 1;
punkt.y = 1;
cout << "Wartosc pola x z obiektu punkt to ";
cout << punkt.x << endl;
cout << "Wartosc pola y z obiektu punkt to ";
cout << punkt.y << endl;
system("pause");
return 0;
}
Aby program był w pełni funkcjonalny, należy dopisać funkcję main z odpowiednim
kodem.
Program Punkt.cpp.
Po wykonaniu instrukcji TPunkt punkt, w pamięci komputera powstanie obiekt punkt
klasy TPunkt.
Można powiedzieć, że punkt to zmienna typu obiektowego TPunkt, albo że punkt
jest instancją (wystąpieniem) klasy TPunkt. (w skrócie będziemy używali słowa obiekt
zamiast zmienna typu obiektowego i klasa zamiast typ obiektowy)
Gdyby w programie napisać:
TPunkt punkt1;
TPunkt punkt2;
to utworzone zostaną dwa obiekty (dwie instacje) klasy TPunkt.
Operator „kropka” pozwala na bezpośredni dostęp do pól obiektu punkt. Instrukcja
punkt.x = 1 pozwala na przypisanie wartości 1 do pola x w obiekcie punkt.
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II. 11
______________________________________________________________________
©2006 Jerzy Kluczewski
int main()
{
TPunkt pierwszyPunkt, drugiPunkt;
pierwszyPunkt.x = 100;
pierwszyPunkt.y = 200;
drugiPunkt.x = 300;
drugiPunkt.y = 400;
cout << "Wartosci pol obiektu pierwszyPunkt:" << endl;
cout << "x= "<< pierwszyPunkt.pobierzX() << ", ";
cout << "y= "<< pierwszyPunkt.pobierzY() << endl;
cout << "Wartosci pol obiektu drugiPunkt:" << endl;
cout << "x= "<< drugiPunkt.pobierzX() << ", ";
cout << "y= "<< drugiPunkt.pobierzY() << endl;
return 0;
}
Odwołania do składowych obiektu
Stosowanie obiektów pozbawione byłoby sensu, gdyby nie można było się odwoływać
(odczytywać lub zapisywać do pól obiektu) do ich składowych lub też wywoływać ich
metody.
Do tego celu stosujemy operator
(kropka). Na przykład dla zmiennej obiektowej
punkt do jej pola x należy wpisać wartość 20 – wykonuje się to za pomocą następującej
instrukcji:
punkt.x = 10;
Odczyt wartości pola x ze zmiennej obiektowej punkt – wykonuje się to za pomocą
następującej instrukcji:
wartość = punkt.x;
Podobnie jest w przypadku metod. Wywołanie metody polega na podaniu nazwy
zmiennej obiektowej, znaku kropki i nazwy metody n.p.
int wspX = punkt.pobierzX();
Wiele obiektów tej samej klasy
W jednym programie może istnieć wiele obiektów tej samej klasy. Program
Punkt2.cpp demonstruje taką sytuację. Do odczytu zawartości pól obiektu
wykorzystywane są metody pobierzX, pobierzY. Za pomocą kodu programu tworzone
są dwa obiekty pierwszyPunkt, drugiPunkt, przypisywane są różne wartości do pól
obiektów, a za pomocą odpowiednich metod uzyskiwane są wartości tych pól.
Program Punkt2.cpp.
12
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II.
______________________________________________________________________
______________________________________________________________________
©2006 Jerzy Kluczewski
IV.
Właściwości obiektu
Za pomocą metod można zmieniać właściwości obiektu. W poprzednim przykładzie
klasa TPunkt nie posiadała takich możliwości. Nic nie stoi na przeszkodzie, aby w
klasie dopisać metody ustawiające wartości pól x i y. Metody te muszą jednak mieć
możliwość pobierania argumentów (czyli wartości, które będą przesyłały do wnętrza
obiektu). Metody te tworzą tzw. interfejs klasy, pozwalający na dostęp do obiektów tej
klasy (Rys. 3).
Rys. 3. Metody jako interfejs dostępu programu do pól obiektu.
Argumenty metod
Każda metoda, tak jak funkcja w C++, może przyjmować argumenty, czyli dane.
Argumenty umieszcza się w nawiasach okrągłych, oddzielając je od siebie znakami
przecinka. Oto schemat takiej konstrukcji:
typ_zwracanego wyniku
nazwa_metody
(typ_1 argument_1, typ_2 argument_2,
… typ_n argument_n)
Przykład interfejsu do obiektu klasy punkt (Rys.4).
Rys. 4. Metody ustawiające wartości pól obiektu klasy TPunkt.
Pole y
Pole x
Metody:
pobierzX
pobierzY
ustawX
ustawY
Obiekt punkt
Program
główny
dostęp
void
ustawX
(int wspX)
void
ustawY
(int wspY)
wsp
X
wsp
Y
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II. 13
______________________________________________________________________
©2006 Jerzy Kluczewski
Projekt zmodyfikowanej klasy TPunkt będzie teraz wyglądał następująco:
Plik Punkt.h
Natomiast definicje metod w programie Punkt3.cpp będą wyglądały następująco:
Plik Punkt3.cpp
class TPunkt
{
public:
int x;
int y;
int pobierzX();
int pobierzY();
void ustawX(int wspX);
void ustawY(int wspY);
};
#include <iostream>
#include "Punkt.h"
using namespace std;
int TPunkt::pobierzX()
{
return x;
}
int TPunkt::pobierzY()
{
return y;
}
void TPunkt::ustawX(int wspX)
{
x = wspX;
}
void TPunkt::ustawY(int wspY)
{
y = wspY;
}
14
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II.
______________________________________________________________________
______________________________________________________________________
©2006 Jerzy Kluczewski
void TPunkt::ustawX(int x)
{
x = x;
}
void TPunkt::ustawX(int x)
{
this->x = x;
}
void TPunkt::ustawY(int y)
{
this->y = y;
}
Odwołanie this
Co się stanie, gdy w metodzie danej klasy użyjemy argumentu o identycznej nazwie jak
jedno z pól, (patrz przykład poniżej) ?
Powyższa definicja metody jest formalnie poprawna, lecz jest bez sensu!!! Aby
odróżnić w niej nazwę pola x klasy TPunkt od nazwy argumentu x, należy posłużyć się
słowem kluczowym this, które oznacza obiekt bieżący (jest synonimem obiektu
bieżącego). Należy zastosować konstrukcję
this
nazwa_pola.
Operator
jest równoznaczny z operatorem (kropka), dlatego w tym przypadku
operacja this
x jest traktowana tak samo jak punkt.x. Poprawne użycie argumentu x
w definicji metod ustawX, ustawY wygląda następująco:
Konstrukcja
Interpretacja konstrukcji
int x;
zmienna x typu int
this
x
pole x w aktualnym obiekcie
punkt.x
pole x w obiekcie punkt
Podobnie można użyć operatora
dla innych składowych obiektu, na przykład:
this
y = 10;
x = this
pobierzX();
this
ustawY(5);
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II. 15
______________________________________________________________________
©2006 Jerzy Kluczewski
void ustawWspolrzedne(TPunkt punkt);
void TPunkt::ustawWspolrzedne(TPunkt punkt)
{
x = punkt.x;
y = punkt.y;
}
Sekwencja działania instrukcji pierwszyPunkt.ustawX(100); na obiekcie
pierwszyPunkt klasy TPunkt wygląda następująco.
Rys. 5. Ustawianie wartości pola x obiektu pierwszyPunkt.
V.
Przeciążanie metod
Obiekt jako argument
Argumentem przekazywanym metodzie może być również obiekt, na przykład:
Definicja metody:
pierwszyPunkt
pierwszyPunkt.ustawX(100);
void TPunkt::ustawX(int x)
zmienna x ma wartość 100
100
100
Pole x
Pole y
100
?
16
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II.
______________________________________________________________________
______________________________________________________________________
©2006 Jerzy Kluczewski
class TPunkt
{
public:
int x;
int y;
int pobierzX();
int pobierzY();
void ustawX(int wspX);
void ustawY(int wspY);
void ustawWspolrzedne(TPunkt punkt);
};
#include <iostream>
#include "Punkt5.h"
using namespace std;
int TPunkt::pobierzX()
{ return x; }
int TPunkt::pobierzY()
{ return y; }
void TPunkt::ustawX(int wspX)
{ x = wspX; }
void TPunkt::ustawY(int wspY)
{ y = wspY; }
void TPunkt::ustawWspolrzedne(TPunkt punkt)
{ x = punkt.x; y = punkt.y; }
int main()
{
TPunkt pierwszyPunkt, drugiPunkt;
pierwszyPunkt.ustawX(100);
pierwszyPunkt.ustawY(200);
drugiPunkt.ustawWspolrzedne(pierwszyPunkt);
cout << "x= "<< pierwszyPunkt.pobierzX() << ", ";
cout << "y= "<< pierwszyPunkt.pobierzY() << endl;
cout << "x= "<< drugiPunkt.pobierzX() << ", ";
cout << "y= "<< drugiPunkt.pobierzY() << endl;
system("pause");
return 0;
}
Przykład programu wykorzystującego ten sposób przekazywania parametrów:
Plik Punkt5.h
Plik Punkt5.cpp
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II. 17
______________________________________________________________________
©2006 Jerzy Kluczewski
class TPunkt
{
public:
int x;
int y;
int pobierzX();
int pobierzY();
void ustawX(int wspX);
void ustawY(int wspY);
void ustawWspolrzedne(TPunkt punkt);
void ustawWspolrzedne(int wspX, int wspY);
};
void TPunkt::ustawWspolrzedne(TPunkt punkt)
{
x = punkt.x;
y = punkt.y;
}
void TPunkt::ustawWspolrzedne(int wspX, int wspY)
{
x = wspX;
y = wspY;
}
Overloading
W języku C++ możliwe jest tzw. przeciążanie metod (ang. overloading). Polega ono na
zdefiniowaniu wielu metod o tej samej nazwie, ale różniących się argumentami
wywołania. Otóż w jednej klasie może istnieć wiele metod o takich samych nazwach,
co nie znaczy wcale że są to te same metody. Różnią się one bowiem listą argumentów
przekazywanych w trakcie ich wywoływania. Tą możliwość języka C++ nazywamy
przeciążaniem metod.
Oto przykład zmodyfikowanej deklaracji klasy TPunkt zawierającej dwie metody o tej
samej nazwie void ustawWspolrzedne :
Plik Punkt5.h
Argumenty oraz kody tych metod różnią się między sobą i wyglądają następująco:
Plik Punkt5.cpp
18
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II.
______________________________________________________________________
______________________________________________________________________
©2006 Jerzy Kluczewski
#include <iostream>
#include "Punkt5.h"
using namespace std;
int TPunkt::pobierzX()
{
return x;
}
int TPunkt::pobierzY()
{
return y;
}
void TPunkt::ustawX(int wspX)
{
x = wspX;
}
void TPunkt::ustawY(int wspY)
{
y = wspY;
}
void TPunkt::ustawWspolrzedne(TPunkt punkt)
{
x = punkt.x;
y = punkt.y;
}
void TPunkt::ustawWspolrzedne(int wspX, int wspY)
{
x = wspX;
y = wspY;
}
int main()
{
TPunkt pierwszyPunkt, drugiPunkt;
pierwszyPunkt.ustawWspolrzedne(100, 200);
drugiPunkt.ustawWspolrzedne(pierwszyPunkt);
cout << "x= "<< pierwszyPunkt.pobierzX() << ", ";
cout << "y= "<< pierwszyPunkt.pobierzY() << endl;
cout << "x= "<< drugiPunkt.pobierzX() << ", ";
cout << "y= "<< drugiPunkt.pobierzY() << endl;
system("pause");
return 0;
}
Po skompilowaniu i uruchomieniu poniższego przykładowego programu Punkt5.cpp:
Na ekranie pojawią się następujące wyniki:
x= 100, y= 200
x= 100, y= 200
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II. 19
______________________________________________________________________
©2006 Jerzy Kluczewski
void FunA(int X)
{
X++;
}
VI.
Sposoby przekazywania argumentów
Argumenty metod mogą być przekazywane na dwa sposoby:
•
przez wartość (ang. value)
•
przez referencję (ang. reference)
Sposób pierwszy polega na tym, że wartość przekazywana funkcji (zmienna K) nie
ulega zmianie po zakończeniu działania funkcji funA. Wewnątrz funkcji FunA nie ma
dostępu do zmiennej K.
Rys. 6. Przekazywanie wartości argumentu X przez wartość.
Definicja funkcji funA:
FunA(K);
void FunA(int X)
zmienna X ma wartość 10
zmienna K ma wartość 10
10
10
K = 10;
zmienna K ma wartość 10
X++;
zmienna X ma wartość 11
zmienna K ma wartość 10
cout << K << endl;
zmienna K ma wartość 10
20
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II.
______________________________________________________________________
______________________________________________________________________
©2006 Jerzy Kluczewski
void FunB(int &X)
{
X++;
}
•
Sposób przez referencję
Sposób drugi polega na tym, że wartość przekazywana funkcji (zmienna K) ulega
zmianie po zakończeniu działania funkcji, zostaje zmodyfikowana przez funkcję funB.
Wewnątrz funkcji FunB wszelkie zmiany dokonywane na zmiennej X odbywają się tak
jakby to była zmienna K.
Rys. 7. Przekazywanie wartości argumentu X przez referencję.
Definicja funkcji funB:
Obie funkcje FunA i FunB na pozór różnią się nieznacznie a mianowicie jednym
znakiem &. Znak ten oznacza operator referencji.
FunB(K);
void FunB(int &X)
zmienna K jest traktowana tak
samo jak zmienna X
10
10
K = 10;
zmienna K ma wartość 10
X++;
zmienna X ma wartość 11
zmienna K ma wartość 11
cout << K << endl;
zmienna K ma wartość 11
11
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II. 21
______________________________________________________________________
©2006 Jerzy Kluczewski
klasa* nazwa_obiektu = new klasa();
TPunkt* punkt = new TPunkt();
(*punkt).x = 100;
VII.
Dynamiczne obiekty
Aby utworzyć zmienną dynamiczną, korzysta się z operatora new w następującej
konstrukcji:
typ* nazwa_zmiennej = new typ;
Przykład:
int* pLiczba = new int;
Odwołanie do miejsca wskazywanego przez pLiczba odbywa się za pomocą operatora
*, na przykład:
*pLiczba = 100;
W przypadku klas i obiektów jest podobnie. Aby dynamicznie utworzyć nowy obiekt
danej klasy, należy zastosować konstrukcję:
Dla klasy TPunkt zdefiniowanej w poprzednich rozdziałach, dynamiczny obiekt
będzie tworzony za pomocą następującej instrukcji:
W tym przypadku nazwa punkt nie będzie obiektem, tylko wskaźnikiem do obiektu.
Odwoływanie się do składowych obiektu do którego nie ma bezpośredniego dostępu,
jest już trochę trudniejsze, polega ono na użyciu:
operatora wyłuskania (*),
operatora dostępu do składowych (
)
oraz nawiasów okrągłych.
Przypisanie składowej x (pole x) w obiekcie wskazywanym przez wskaźnik punkt,
wartości 100 będzie miało następująca postać:
Po lewej stronie znaku = wykonane będą kolejno następujące czynności: wyłuskanie
obiektu, a potem udostępnione pole x.
22
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II.
______________________________________________________________________
______________________________________________________________________
©2006 Jerzy Kluczewski
(*punkt).ustawX(100);
wskaźnik
nazwa_pola
wskaźnik
nazwa_metody(argumenty)
punkt)
x = 100;
punkt)
ustawX(100);
void Punkt::inicjuj()
{
this
x = 0;
this
y = 0;
}
Z kolei uruchomienie metody ustawX na obiekcie wskazywanym przez wskaźnik
punkt, będzie miało następująca postać:
Istnieje jeszcze inny sposób dostępu (częściej stosowany) do składowych obiektu –
polega on na użyciu operatora
.
Dostęp do obiektu danej klasy, wskazywanego przez wskaźnik wykonuje się stosując
następującą konstrukcję:
lub
Przykłady użycia tej drugiej metody:
Przypomnienie: gdy obiekty lub zmienne proste są tworzone dynamicznie za pomocą
operatora new, koniecznie należy pamiętać, aby po ich wykorzystaniu usunąć je ze
sterty za pomocą operatora delete. W przeciwnym razie nastąpią tzw. wycieki pamięci.
Inicjalizacja pól
W momencie tworzenia obiektu, warto wiedzieć jakie wartości początkowe mają jego
pola. Wartości początkowe zależą od kompilatora, i zazwyczaj mają one wartości
trudne do przewidzenia. Problem ten można usunąć pisząc własna metodę inicjalizacji
np.
Ale również może się zdarzyć, że programista zapomni o jej wywołaniu, nie jest to
dobre rozwiązanie.
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II. 23
______________________________________________________________________
©2006 Jerzy Kluczewski
class nazwa_klasy
{
public:
// definicje pól
nazwa_klasy
();
// definicje metod
};
class TPunkt
{
public:
int x;
int y;
TPunkt();
// definicje pozostałych metod
};
TPunkt ::TPunkt()
{
x = 0;
y = 0;
}
Konstruktor
Z pomocą w inicjowaniu pól obiektów przychodzi nam konstruktor.
Konstruktor to specjalna metoda wywoływana automatycznie podczas tworzenia
obiektu, nadaje się zatem doskonale do jego wstępnej inicjalizacji.
Tworząc dobrze przemyślany konstruktor, nie trzeba się przejmować inicjalizowaniem
wartości składowych podczas tworzenia obiektu.
Metoda będąca konstruktorem nie zwraca żadnego wyniku. Przed nazwą konstruktora
nie może pojawić się nawet słowo void. Nazwa takiej metody musi być identyczna z
nazwą klasy, której dotyczy. Konstruktor wchodzi w skład klasy.
Ogólna deklaracja klasy posiadającej konstruktor wygląda następująco
Przykład deklaracji klasy TPunkt
Przykład definicji konstruktora TPunkt
24
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II.
______________________________________________________________________
______________________________________________________________________
©2006 Jerzy Kluczewski
class nazwa_klasy
{
public:
// definicje pól
~
nazwa_klasy
();
// definicje metod
};
class TPunkt
{
public:
int x;
int y;
~
TPunkt();
// definicje pozostałych metod
};
TPunkt ::
~
TPunkt()
{
cout<<”Wywołano destruktor”<< endl;
}
Destruktor
Konstruktory to metody wykonywane podczas tworzenia nowego obiektu. Z kolei
destruktory to metody wykonywane podczas usuwania obiektu (wykonanie instrukcji
delete dla obiektów tworzonych dynamicznie lub gdy obiekt będzie usuwany z pamięci
podczas kończenia pracy programu – dla obiektów statycznych).
Aby zdefiniować destruktor używa się podobnej konstrukcji jak dla konstruktora, tylko
przed jego nazwą umieszcza się znak
~
(tylda).
Ogólna deklaracja klasy posiadającej konstruktor wygląda następująco
Przykład deklaracji klasy TPunkt
Przykład definicji konstruktora
~
TPunkt
Destruktor przydaje się w sytuacjach gdy trzeba posprzątać po obiekcie. Gdy podczas
pracy obiektu, konstruktor zarezerwuje pamięć lub inne zasoby systemowe, to przed
usunięciem obiektu należy za pomocą destruktora wszystkie jego zasoby zwolnić.
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II. 25
______________________________________________________________________
©2006 Jerzy Kluczewski
// Program demonstracyjny
#include <iostream>
using namespace std;
class Example
{
public:
int* pole1;
Example()
{
pole1 = new int;
*pole1 = 0;
cout <<"Wywolanie konstruktora"<<endl;
}
~Example()
{
delete pole1;
cout <<"Wywolanie destruktora"<<endl;
}
};
int main()
{
Example example;
system("pause");
return 0;
}
Poniższy program DemoUnit1.cpp demonstruje działanie konstruktora i destruktora
Program DemoUnit1.cpp
Zadanie VII-1
W funkcji main() dopisz dwie instrukcje: zapisującą wartość 100 do wartości
wskazywanej przez pole1 w obiekcie example, odczytującą wartość wskazywaną przez
pole1 z obiektu example.
Zadanie VII-2
W jaki sposób w funkcji main() będzie wyglądał zapis i odczyt wartości wskazywanej
przez pole1 obiektu klasy Example, jeśli zostanie on utworzony przy pomocy
instrukcji Example *example = new Example() ?
26
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II.
______________________________________________________________________
______________________________________________________________________
©2006 Jerzy Kluczewski
class TPunkt
{
public:
int x;
int y;
TPunkt(int x, int y);
};
TPunkt::TPunkt( int x = 0, int y = 0);
{
this
x = x;
this
y = y;
};
Zadanie VII-3
Jak będzie wyglądał kod klasy TPunkt w którym wartości x i y byłyby przechowywane
na stercie, a w obiektach klasy TPunkt znajdowałyby się tylko wskaźniki int* x, int* y.
Zadanie VII-4
Proszę napisać kod klasy TKontener, zachowująca się podobnie jak dynamiczna
tablica przechowująca dodatnie wartości całkowite. Będzie ona miała dwie metody
get() i set() pobierające i ustawiające wartości poszczególnych komórek. Rozmiar
tablicy będzie się w razie konieczności zwiększał automatycznie podczas dodawania
nowych danych. Przykładowo set(2,10) ma spowodować przypisanie wartości 10 do
drugiej komórki, a get(20) ma pobrać wartość dwudziestej komórki.
VIII.
Konstruktory
Argumenty konstruktorów
Konstruktory, tak jak inne metody mogą być bezargumentowe lub przyjmować
argumenty. Mogą też mieć argumenty domyślne. Deklaracja klasy TPunkt zawierającej
konstruktor może wyglądać następująco:
a definicja konstruktora:
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II. 27
______________________________________________________________________
©2006 Jerzy Kluczewski
#include <iostream>
#include "konstruktor1Unit.h"
using namespace std;
TPunkt::TPunkt( int x = 0, int y = 0)
{
This
x = x;
This
y = y;
};
int main()
{
TPunkt punkt1;
TPunkt punkt2(100);
TPunkt punkt3(200, 300);
cout << "punkt1: "<<endl;
cout << "x= "<<punkt1.x<<" y= "<<punkt1.y<<endl;
cout << "punkt2: "<<endl;
cout << "x= "<<punkt2.x<<" y= "<<punkt2.y<<endl;
cout << "punkt3: "<<endl;
cout << "x= "<<punkt3.x<<" y= "<<punkt3.y<<endl;
cout << "punkt4: "<<endl;
cout << "x= "<<punkt4
x<<" y= "<<punkt4
y<<endl;
system("pause");
return 0;
}
Program demonstrujący trzy sposoby tworzenia obiektu klasy TPunkt:
•
nie podając żadnego argumentu TPunkt punkt1,
•
podając tylko jeden argument TPunkt punkt2(100),
•
podając oba argumenty TPunkt punkt3(200, 300).
Program konstruktory1Unit.cpp
Wynik działania programu:
punkt1:
x= 0 y= 0
punkt2:
x= 100 y= 0
punkt3:
x= 200 y= 300
punkt4:
x= 1 y= 2
W przypadku czwartego punktu, na stercie zainicjalizowano obszar, zawierający dwa
pola x i y , które zainicjowano wartościami 1 i 2. Nazwa punkt4 jest wskaźnikiem do
tego obszaru (obiektu).
28
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II.
______________________________________________________________________
______________________________________________________________________
©2006 Jerzy Kluczewski
#include <iostream>
#include "konstruktor2Unit.h"
using namespace std;
TPunkt::TPunkt( int x = 0, int y = 0)
{
this->x = x;
this->y = y;
};
int main()
{
TPunkt punkt1(100, 200);
TPunkt punkt2(punkt1);
cout << "punkt1: "<<endl;
cout << "x= "<<punkt1.x<<" y= "<<punkt1.y<<endl;
cout << "punkt2: "<<endl;
cout << "x= "<<punkt2.x<<" y= "<<punkt2.y<<endl;
system("pause");
return 0;
}
Konstruktor kopiujący
Do stworzenia kilku obiektów tej samej klasy można użyć konstruktora kopiującego.
Co to jest konstruktor kopiujący? Konstruktor kopiujący to taki konstruktor, którego
zadaniem jest utworzenie obiektu będącego kopią obiektu już istniejącego.
Jeśli konstruktor kopiujący nie zostanie zdefiniowany oddzielnie, to i tak zostanie on
automatycznie stworzony – oto przykład programu zawierającego konstruktor
automatyczny (program konstruktory2Unit.cpp):
Program konstruktory2Unit.cpp
Instrukcja TPunkt punkt1(100, 200); spowodowała utworzenie obiektu punkt1 o
współrzędnych x=100 i y=200. następna instrukcja TPunkt punkt2(punkt1);
utworzyła punkt2 obiekt klasy TPunkt, będący kopią obiektu punkt1. Dlatego jego
pola x i y zawierają te same wartości co punkt1.
Uwaga:
Nawet jeśli nie umieszczono w kodzie własnego konstruktora kopiującego, to i tak
zostanie on dodany automatycznie. Jego działanie ogranicza się tylko do skopiowania
obszaru pamięci zajmowanego przez obiekt źródłowy (punkt1) do obszaru
zajmowanego przez obiekt docelowy (punkt2).
Zagadnienie konstruktora kopiującego nabiera odpowiedniego stopnia powagi, gdy
zmienimy definicje pól w klasie z typu prostego int na wskaźniki do typu int.
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II. 29
______________________________________________________________________
©2006 Jerzy Kluczewski
class TPunkt
{
public:
int *x;
int *y;
TPunkt(int x, int y)
{
this
x = new int(x);
this
y = new int(y);
};
~
TPunkt()
{
delete this
x;
delete this
y;
};
};
Konstruktor kopiujący i pola wskaźnikowe
W tym rozdziale nastąpi zasadnicza zmiana podejścia do konstruktorów i wskaźników.
W poniższym przykładzie definicja klasy TPunkt będzie zawierać pola x i y będące
wskaźnikami do typu int. Ze względu na charakter pól klasy, ulegną zmianie definicje
konstruktora i destruktora.
Plik konstruktor3Unit.h.
Konstruktor tworzy na stercie dwa obszary typu int, ładuje do nich wartości
argumentów x i y, a do pól x i y przypisuje wskaźniki do zarezerwowanych obszarów.
Destruktor usuwa ze sterty dwa obszary wskazywane przez wskaźniki x i y.
Reasumując, nazwy x i y nie przechowują danych, lecz wskaźniki do danych, które są
zapamiętane na stercie.
Zadanie VIII-1
Demonstracja podejścia wskaźnikowego.
Zmień program konstruktor2Unit.cpp na program konstruktor3Unit.cpp w ten
sposób, aby zmienić w funkcji main(), sposób dostępu do składowych obiektów:
30
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II.
______________________________________________________________________
______________________________________________________________________
©2006 Jerzy Kluczewski
int main()
{
TPunkt punkt1(100, 200);
TPunkt punkt2(punkt1);
cout << "punkt1: "<<endl;
cout << "x= "<<*(punkt1.x)<<" y= "<<*(punkt1.y)<<endl;
*(punkt2.x) = 1;
*(punkt2.y) = 2;
cout << "punkt1: "<<endl;
cout << "x= "<<*(punkt1.x)<<" y= "<<*(punkt1.y)<<endl;
system("pause");
return 0;
}
Zmień zawartość funkcji main() na następującą:
Po kompilacji i uruchomieniu programu konstruktor3Unit.cpp, nastąpi niemiła
niespodzianka – program wykonał nieprawidłowe operacje i nastąpiła interwencja
systemu operacyjnego.
Rys. 8. Komunikat informujący że w destruktorze próbowano zwolnić pamięć, która już
wcześniej została zwolniona.
Druga niespodzianka to fakt, że za pomocą instrukcji
*(punkt2.x) = 1;
*(punkt2.y) = 2;
miał zostać zmodyfikowany punkt2, a okazało się, że został zmodyfikowany punkt1.
Rys. 9. Komunikat pokazujący wcześniejsze wartości x i y oraz późniejsze wartości x i
y obiektu punkt1.
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II. 31
______________________________________________________________________
©2006 Jerzy Kluczewski
Aby przerwać działanie programu i wrócić do jego projektowania należy użyć
polecenia Run
Program reset (Ctrl+F2).
Przyczyna błędu EAccessViolation:
W instrukcji delete this
x; destruktora dla obiektu punkt2, próbuje się zwolnić
pamięć już zwolnioną w trakcie uruchomienia destruktora dla obiektu punkt1.
Przyczyna błędu polegającego na złych wynikach:
Problem powstał gdy domyślny konstruktor kopiujący skopiował wskaźniki z obiektu
punkt1 do obiektu punkt2. (Rys. 10). Skutkiem tego niedbalstwa, wskaźnik x oraz y po
prostu rozróżnia obiektu (nie ważne czy należy do obiektu punkt1, czy punkt2, zawsze
wskazuje na ten sam obszar). Natomiast wartości wskazywane nie zostały skopiowane.
Rys. 10. Obiekty punkt1, punkt2 zawierają wskaźniki wskazujące na te sam obszar.
Aby program działał poprawnie, należy napisać własny konstruktor kopiujący, który dla
każdego tworzonego jego za pomocą. obiektu będzie rezerwował oddzielną pamięć.
W pliku konstruktor3Unit.h za definicją konstruktora TPunkt(int x, int y) dopisać
należy nową (własną) definicję konstruktora kopiującego :
TPunkt(TPunkt &punkt) // konstruktor kopiujący
{
this->x = new int( *(punkt.x) );
this->y = new int( *(punkt.y) );
};
punkt1
punkt2
int *x
int *y
int *x
int *y
obszar x
obszar y
32
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II.
______________________________________________________________________
______________________________________________________________________
©2006 Jerzy Kluczewski
class TPunkt
{
public:
int *x;
int *y;
TPunkt(int x, int y)
{
this->x = new int(x);
this->y = new int(y);
};
TPunkt(TPunkt &punkt) // konstruktor kopiujący
{
this->x = new int(*(punkt.x));
this->y = new int(*(punkt.y));
};
~TPunkt()
{
delete this->x;
delete this->y;
};
};
Tworzenie poprawnego konstruktora kopiującego ilustruje poniższy plik
konstruktor3Unit.h oraz Rys. 11.
Plik konstruktor3Unit.h.
Rys. 11. Obiekty punkt1, punkt2 zawierają wskaźniki wskazujące na osobne obszary.
punkt1
punkt2
int *x
int *y
int *x
int *y
obszar x
obszar y
obszar x
obszar y
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II. 33
______________________________________________________________________
©2006 Jerzy Kluczewski
class nazwa_klasy
{
public:
static nazwa_typu nazwa_pola
;
};
nazwa_typu
nazwa_klasy
::
nazwa_pola
;
nazwa_typu
nazwa_klasy
::
nazwa_pola
= wartosc_poczatkowa;
IX.
Składowe statyczne
Klasy mogą zawierać składowe statyczne, czyli takie pola i metody, które są dostępne,
(wspólne) dla wszystkich obiektów danej klasy. Służą one do komunikowania się
obiektów między sobą.
Pola statyczne
Aby umieścić statyczne pole wewnątrz klasy, należy wykonać następujące czynności:
•
zadeklarować pole wewnątrz klasy
Ogólna deklaracja klasy posiadającej pole statyczne wygląda następująco
•
zdefiniować pole na zewnątrz klasy (poza deklaracją klasy) – czyli
zarezerwować pamięć dla zmiennej statycznej. Należy wykorzystać operator
zasięgu ::
Ogólna definicja pola (zmiennej) statycznej wygląda następująco
Od tego miejsca w programie faktycznie pole statyczne istnieje i można się do niego
odwoływać za pomocą operatora zakresu
::
albo operatora
(kropka).
Tak zdefiniowana zmienna statyczna ma wartość początkową równa 0 (wykonuje to
kompilator) Aby ją zainicjować inną wartością należy użyć konstrukcji
34
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II.
______________________________________________________________________
______________________________________________________________________
©2006 Jerzy Kluczewski
//program4Unit.cpp
#include <iostream>
using namespace std;
class Klasa
{
public:
static int liczba;
};
int Klasa::liczba;
int main()
{
Klasa::liczba = 10;
cout << "Klasa::liczba="<<Klasa::liczba<<endl;
cout<<endl;
Klasa example1;
Klasa example2;
example1.liczba = 20;
cout << "example1.liczba="<<example1.liczba<<endl;
cout << "Klasa::liczba="<<Klasa::liczba<<endl;
example2.liczba = 30;
cout << "example2.liczba="<<example2.liczba<<endl;
cout << "Klasa::liczba="<<Klasa::liczba<<endl;
system("pause");
return 0;
}
Przykład deklaracji klasy Klasa zawierającej pole statyczne liczba i definicji pola
statycznego liczba ilustruje program program4Unit.cpp, można przekonać się, że
odwołanie do pola statycznego poprzez dowolny obiekt example1 lub example2 jest
równoznaczne z odwołaniem się poprzez operator zasięgu
::
Program program4Unit.cpp
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II. 35
______________________________________________________________________
©2006 Jerzy Kluczewski
class nazwa_klasy
{
public:
static typ_zwracany nazwa_metody
(argumenty_metody);
};
typ_zwracany
nazwa_klasy
::
nazwa_metody
(argumenty_metody)
{
// kod metody
}
Metody statyczne
W języku C++ mogą istnieć również statyczne metody. Każda statyczna metoda jest
wspólna dla wszystkich obiektów danej klasy.
Aby umieścić statyczne metodę wewnątrz klasy, należy wykonać następujące
czynności:
•
zadeklarować metodę wewnątrz klasy
Ogólna deklaracja klasy posiadającej metodę statyczną wygląda następująco
•
zdefiniować jej kod na zewnątrz klasy. Należy wykorzystać operator zasięgu ::
Ogólna definicja metody statycznej wygląda następująco
Przykład deklaracji i definicji metody statycznej Display(), która będzie wyświetlała
wartość pola statycznego liczba, zawiera zmodyfikowany program program4Unit.cpp,
którego treść umieszczono na następnej stronie.
Dostęp do pól i metod
Zwykłe metody mają dostęp do wszystkich danych klasy (dostęp do wszystkich pól).
Metody statyczne maja dostęp jedynie do danych statycznych, czyli mogą :
•
odwoływać się tylko do pól statycznych
•
wywoływać tylko inne metody statyczne
36
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II.
______________________________________________________________________
______________________________________________________________________
©2006 Jerzy Kluczewski
//program4Unit.cpp
#include <iostream>
using namespace std;
class Klasa
{
public:
static int liczba;
static void Display();
};
int Klasa::liczba;
void Klasa::Display()
{
cout << "(metoda) liczba = " << liczba << endl;
}
int main()
{
Klasa::liczba = 10;
cout << "Klasa::liczba="<<Klasa::liczba<<endl;
cout<<endl;
Klasa example1;
Klasa example2;
example1.liczba = 20;
cout << "example1.liczba="<<example1.liczba<<endl;
cout << "Klasa::liczba="<<Klasa::liczba<<endl;
example1.Display();
example2.liczba = 30;
cout << "example2.liczba="<<example2.liczba<<endl;
cout << "Klasa::liczba="<<Klasa::liczba<<endl;
example2.Display();
system("pause");
return 0;
}
Program zmodyfikowany program4Unit.cpp:
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II. 37
______________________________________________________________________
©2006 Jerzy Kluczewski
#include <iostream>
using namespace std;
class Klasa
{
public:
int a;
static int b;
void fun(void);
void funA();
void funB();
static void funAA();
static void funBB();
};
int Klasa::b = 1;
void Klasa::funA(void)
{
a = 10;
b = 20;
funB();
funAA();
}
void Klasa::funAA()
{
a = 100; // odwołanie do niestatycznego pola
b = 200;
funB(); // odwołanie do niestatycznej metody
funBB();
}
void Klasa::funB() {}
void Klasa::funBB() {}
int main()
{ return 0; }
Dostęp do pól i metod – c.d.
Oto program program5Unit.cpp ilustrujący sytuację odwołania się z poziomu metody
statycznej, do niestatycznych obiektów.
Program program5Unit.cpp:
38
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II.
______________________________________________________________________
______________________________________________________________________
©2006 Jerzy Kluczewski
Po kompilacja programu program5Unit.cpp zakończy się dwoma komunikatami
program5Unit.cpp: E2231 Member Klasa::a Cannot be used whitout an object.
program5Unit.cpp: E2283 Member Use . or -> to call Klasa::funB().
Pierwszy błąd powstał w instrukcji
a = 100;
spowodowany tym, że metoda statyczna funAA odwołuje się do niestatycznego pola a.
Drugi błąd powstał w instrukcji
funB();
spowodowany tym, że metoda zwykła funB() jest wywoływana w metodzie statycznej
funAA().
Zadanie IX-1
Napisz kod przykładowej klasy TOsoba, której zadaniem będzie przechowywanie
imion i nazwisk osób. Pola imie i nazwisko powinny być typu char* , a pamięć
niezbędna do przechowywania danych ma być rezerwowana dynamicznie w
konstruktorze podczas tworzenia obiektu. Pamiętaj o uwzględnieniu konstruktora
kopiującego oraz destruktora.
Zadanie IX-2
Napisz kod przykładowej klasy TNapis, której zadaniem będzie przechowywanie ciągu
znaków. Pole dane powinno być typu char* , a pamięć niezbędna do przechowywania
danych ma być rezerwowana dynamicznie w konstruktorze podczas tworzenia obiektu.
Klasa powinna posiadać metodę o nazwie Dlugosc zwracającą liczbę znaków danej
przechowywanej w polu dane. Pamiętaj o uwzględnieniu konstruktora kopiującego oraz
destruktora.
Zadanie IX-3
Napisz kod przykładowej klasy TKlasa, zawierającej pole statyczne count, którego
zadaniem będzie przechowywanie aktualnej liczby obiektów tej klasy.
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II. 39
______________________________________________________________________
©2006 Jerzy Kluczewski
class TPunkt
{
public:
int x;
int y;
TPunkt();
~TPunkt();
int pobierzX();
int pobierzY();
void ustawX(int wspX);
void ustawY(int wspY);
void ustawWspolrzedne(TPunkt punkt);
void ustawWspolrzedne(int wspX, int wspY);
};
class nazwa_klasy_potomnej : public nazwa_klasy_bazowej
{
public:
// definicja klasy potomnej
};
X.
Dziedziczenie
Dziedziczenie to jedna z podstawowych technik programowania obiektowego.
Dziedziczenie realizowane jest za pomocą symbolu : (dwukropek) , w następującej
konstrukcji
gdzie:
•
klasa potomna (klasa podrzędna, podklasa) to nowa klasa przejmująca
(dziedzicząca) składowe klasy bazowej,
•
klasa bazowa (klasa podstawowa, klasa nadrzędna, nadklasa) to klasa już
zdefiniowana, służąca jako pewien wzorzec dla nowej klasy potomnej.
Przykładem klasy bazowej może być definicja klasy TPunkt – reprezentująca punkt na
płaszczyźnie (plik punkt6.h).
Plik punkt6.h.
Aby utworzyć reprezentację punktu w przestrzeni trójwymiarowej, należałoby stworzyć
definicję nowej klasy o nazwie TPunkt3D. Można ją tworzyć od nowa, by uwzględnić
trzy współrzędne x,y,z.
40
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II.
______________________________________________________________________
______________________________________________________________________
©2006 Jerzy Kluczewski
class TPunkt3D : public TPunkt
{
public:
int z;
TPunkt3D();
~TPunkt3D();
int pobierzZ();
void ustawZ(int wspZ);
void ustawWspolrzedne(TPunkt3D punkt);
void ustawWspolrzedne(int wspZ);
};
Można wykorzystać fakt, że dla klasy TPunkt już zostały zdefiniowane odpowiednie
pola (x, y) oraz napisane odpowiednie metody obsługujące współrzędne (x,y) i dopisać
po prostu nowe pole z oraz metody obsługujące trzeci wymiar (Rys. 13).
Wybór tej drugiej metody nazywamy dziedziczeniem.
Następuje więc dziedziczenie gotowych rozwiązań z klasy bazowej TPunkt
(reprezentującej punkt na płaszczyźnie) do nowej klasy potomnej TPunkt3D.
Ciąg dalszy pliku punkt6.h.
W ten oto sposób programista nie namęczył się wiele, bowiem dopisał tylko:
pole z, oraz metody:
•
TPunkt3D()
•
~TPunkt3D()
•
pobierzZ()
•
ustawZ()
•
ustawWspolrzedne()
Aby przekonać się jak zachowują się obiekty klasy potomnej TPunkt3D wystarczy
napisać krótki program, demonstrujący technikę dziedziczenia – patrz program w pliku
punk6.cpp.
Wynik działania programu to:
Konstruktor TPunkt
Konstruktor TPunkt3D
Konstruktor TPunkt
Konstruktor TPunkt3D
10
20
30
0
0
0
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II. 41
______________________________________________________________________
©2006 Jerzy Kluczewski
#include <iostream>
#include "Punkt6.h"
using namespace std;
//==== klasa bazowa ====
TPunkt::TPunkt()
{
cout<<"Konstruktor TPunkt"<< endl;
x = 0;
y = 0;
}
TPunkt ::~TPunkt()
{
}
Rys. 12. Dziedziczenie składowych klasy TPunkt, do klasy potomnej TPunkt3D.
Plik punk6.cpp
Klasa bazowa TPunkt
int x
int y
TPunkt()
~TPunkt()
pobierzX()
pobierzY()
ustawX()
ustawY()
ustawWspolrzedne()
Klasa potomna TPunkt3D
Składowe
dopisane
Składowe
dziedziczone
Nowe (dopisane):
int z;
TPunkt3D()
~TPunkt3D()
pobierzZ()
ustawZ()
ustawWspolrzedne()
42
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II.
______________________________________________________________________
______________________________________________________________________
©2006 Jerzy Kluczewski
int TPunkt::pobierzX()
{
return x;
}
int TPunkt::pobierzY()
{
return y;
}
void TPunkt::ustawX(int wspX)
{
x = wspX;
}
void TPunkt::ustawY(int wspY)
{
y = wspY;
}
void TPunkt::ustawWspolrzedne(TPunkt punkt)
{
x = punkt.x;
y = punkt.y;
}
void TPunkt::ustawWspolrzedne(int wspX, int wspY)
{
x = wspX;
y = wspY;
}
//==== klasa potomna ====
TPunkt3D::TPunkt3D()
{
cout<<"Konstruktor TPunkt3D"<< endl;
z = 0;
}
TPunkt3D ::~TPunkt3D()
{
}
int TPunkt3D::pobierzZ()
{
return z;
}
void TPunkt3D::ustawZ(int wspZ)
{
z = wspZ;
}
Plik punk6.cpp – ciąg dalszy
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II. 43
______________________________________________________________________
©2006 Jerzy Kluczewski
void TPunkt3D::ustawWspolrzedne(TPunkt3D punkt)
{
z = punkt.z;
}
void TPunkt3D::ustawWspolrzedne(int wspZ)
{
z = wspZ;
}
int main()
{
TPunkt3D punkt1;
punkt1.ustawX(10);
punkt1.ustawY(20);
punkt1.ustawZ(30);
TPunkt3D punkt2;
cout << punkt1.x << endl;
cout << punkt1.y << endl;
cout << punkt1.z << endl;
cout << punkt2.x << endl;
cout << punkt2.y << endl;
cout << punkt2.z << endl;
system("pause");
return 0;
}
Plik punk6.cpp – ciąg dalszy
Stosując dziedziczenie ważną kwestią jest zachowanie się konstruktorów klasy bazowej
i potomnej. Jeśli w klasie bazowej istnieje konstruktor, który można wywołać bez
parametrów (konstruktor domyślny TPunkt), to podczas tworzenia obiektów klasy
potomnej najpierw wykonywany jest konstruktor klasy bazowej a potem konstruktor
klasy potomnej (TPunkt3D).
44
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II.
______________________________________________________________________
______________________________________________________________________
©2006 Jerzy Kluczewski
XI.
Dostęp do składowych klasy
W C++ programista może decydować o sposobie dostępu do składowych klasy. Do tego
celu służą modyfikatory dostępu (ang. access modifiers), zwane również
specyfikatorami. Każda składowa może być:
•
publiczna (public),
•
prywatna (private),
•
chroniona (protected).
Składowe publiczne
Dostęp do składowych publicznych jest nieograniczony, można się do nich odwoływać
bezpośrednio.
Składowe prywatne
Dostęp do składowych prywatnych jest ograniczony tylko i wyłącznie do metod
zdefiniowanych w danej klasie (oraz do funkcji zaprzyjaźnionych).
Składowe chronione
Dostęp do składowych chronione mają tylko metody zdefiniowane w danej klasie i
klasach pochodnych.
Jeśli nie zastosujemy żadnego modyfikatora, to składowe klasy zostaną potraktowane
tak, jakby wystąpił modyfikator private. Poniższe definicje klasy są równoważne:
Aby sprawdzić, czy do składowej prywatnej można się odwołać,
napisz program program1.cpp, który pokaże czy jest to możliwe.
Przy próbie kompilacji programu, kompilator wyświetli błąd
[C++ Error] E2247 ‘Bazowa::x’ is not accessible
class nazwa_klasy
{
// definicja wnętrza klasy
};
class nazwa_klasy
{
private:
// definicja wnętrza klasy
};
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II. 45
______________________________________________________________________
©2006 Jerzy Kluczewski
Jeśli pole x jest prywatne, to instrukcja odwołująca się do niego jest błędna:
obiekt1.x = 100;
program1.cpp
Jeśli do składowych prywatnych nie można się odwoływać bezpośrednio, to w jaki
sposób zmieniać ich wartości? Najlepszym sposobem na to jest dopisanie publicznych
metod operujących na składowych prywatnych. Umieść w klasie Bazowa metody getX
i setX. Pierwsza będzie zwracała wartość pola x. a druga będzie przypisywała polu x
wartość argumentu o nazwie x. Ilustruje to program program2.cpp.
program2.cpp
class Bazowa
{
private:
int x;
public:
int y;
};
int main()
{
Bazowa obiekt1;
obiekt1.x = 100;
obiekt1.y = 200;
};
#include <iostream>
using namespace std;
class Bazowa
{
private:
int x;
public:
int y;
int getX() { return x;};
void setX(int x)
{ this -> x = x; };
};
int main()
{
Bazowa obiekt1;
obiekt1.setX(100);
obiekt1.y = obiekt1.getX();
cout << obiekt1.getX() << endl;
cout << obiekt1.y << endl;
system("pause");
};
46
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II.
______________________________________________________________________
______________________________________________________________________
©2006 Jerzy Kluczewski
XII.
Modyfikatory i dziedziczenie
Sprawdź teraz jak działają modyfikatory przy dziedziczeniu. W programie
program3.cpp definiujemy dwie klasy Bazowa i Pochodna (klasa Pochodna
dziedziczy z klasy Bazowa). W klasie bazowej zdefiniuj trzy składowe:
x – pole prywatne,
y – pole publiczne,
z – pole chronione.
W klasie pochodnej zdefiniuj sześć publicznych metod zajmujących się ustawianiem i
pobieraniem wartości poszczególnych pól. Następnie w funkcji main utwórz obiekt
klasy Pochodna oraz wywołaj jego metody setY i setZ, a na koniec sprawdź, czy
możliwe jest odwołanie się do pól y i z.
program3.cpp
class Bazowa
{
private:
int x;
public:
int y;
protected:
int z;
};
class Pochodna: public Bazowa
{
public:
int getX() {return x; };
int getY() {return y; };
int getZ() {return z; };
void setX(int x) {this->x = x; };
void setY(int y) {this->y = y; };
void setZ(int z) {this->x = z; };
};
int main()
{
Pochodna obiekt1;
obiekt1.setY(100);
obiekt1.setZ(200);
obiekt1.y = obiekt1.z;
obiekt1.y = obiekt1.getZ();
};
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II. 47
______________________________________________________________________
©2006 Jerzy Kluczewski
Przy próbie kompilacji programu program3.cpp, kompilator wyświetli błędy:
[C++ Error] E2247 ‘Bazowa::x’ is not accessible
[C++ Error] E2247 ‘Bazowa::z’ is not accessible
Nieprawidłowości w programie program3.cpp to:
Deklaracje:
są błędne, bo klasa pochodna nie ma dostępu do prywatnych składowych klasy
bazowej.
Instrukcja
jest błędna, ponieważ do pola chronionego nie można się odwoływać bezpośrednio.
UWAGA:
Przeczytaj uważnie tekst w ramce „Reguły budowania klas potomnych” (Rys. 13).
Praktyczny cel stosowania modyfikatorów
Modyfikatory private i protected pozwalają na ukrywanie wnętrza klasy (pola klasy), a
na zewnątrz udostępnienie tylko tzw. interfejsu klasy (metody publiczne). Ukrywanie
wnętrza obiektu (klasy) nazywamy hermetyzacją i jest to jedna z głównych cech
programowania obiektowego. Dla programisty korzystającego z gotowych klas
powinny być dostępne tylko metody operujące na polach (właściwościach) klasy
(obiektu). W ten sposób autor klas może w sposób niezauważalny zmieniać
reprezentację wewnętrzną klasy, a korzystający z niej nie musi się tymi zmianami
przejmować.
int getX() {return x; };
void setX(int x)
{
this->x = x;
};
obiekt1.y = obiekt1.z;
48
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II.
______________________________________________________________________
______________________________________________________________________
©2006 Jerzy Kluczewski
Reguły budowania klas potomnych
Rys. 13. Reguły budowania klas potomnych.
class potomna : modyfikator klasa_bazowa
{
// definicja klasy potomnej
};
Schemat budowania klasy potomnej można ogólnie przedstawić jako:
Oznacza to, że klasa potomna ma wpływ na to, w jaki sposób będą
dziedziczone składowe klasy bazowej. Można zatem zmieniać reguły
dostępu. Jedynymi składowymi, których zachowania nie można zmienić, są
składowe prywatne. W dwóch pozostałych przypadkach obowiązują
następujące reguły:
1.
jeżeli przed nazwą klasy bazowej wystąpi modyfikator public, to w
klasie potomnej wszystkie składowe publiczne pozostają
publicznymi, a wszystkie składowe chronione pozostają chronione.
2.
jeżeli przed nazwą klasy bazowej wystąpi modyfikator private, to w
klasie potomnej wszystkie składowe (publiczne i chronione)
pozostają prywatnymi.
3.
jeżeli przed nazwą klasy bazowej wystąpi modyfikator protected, to
w klasie potomnej wszystkie składowe publiczne stają się
chronionymi, a i chronione pozostają chronionymi.
class Bazowa
{
private:
int x;
public:
int y;
protected:
int z;
};
class Pochodna : private Bazowa
{
};
W przykładowej klasie
Pochodna wszystkie
składowe będą
prywatne i nie będzie
do nich
bezpośredniego
dostępu.
REGUŁY BUDOWANIA KLAS POTOMNYCH
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II. 49
______________________________________________________________________
©2006 Jerzy Kluczewski
Zadanie XII-1
Napisz kod klasy bazowej TOsoba, której zadaniem będzie przechowywanie imion i
nazwisk osób. Pola imie i nazwisko powinny być typu char* , a pamięć niezbędna do
przechowywania danych ma być rezerwowana dynamicznie w konstruktorze podczas
tworzenia obiektu. Pamiętaj o konstruktorach zwykłych i kopiujących. Napisz klasę
pochodną TPracownik, dziedziczącej pola i konstruktory klasy TOsoba oraz
przechowującej w polu stanowisko (char* stanowisko) określające stanowisko
zajmowane przez pracownika. Dopisz odpowiednie konstruktory:
TPracownik(char* imie, char* nazwisko, char* stanowisko); //konstruktor
TPracownik(TPracownik &pracownik); //konstruktor kopiujący
~TPracownik(); // destruktor
XIII.
Przesłanianie składowych klasy
Podczas dziedziczenia cech klasy bazowej, klasa pochodna przejmuje składowe klasy
bazowej. Co się jednak stanie, gdy w klasie pochodnej zdefiniujemy składową o takiej
samej nazwie?
Może spowoduje to konflikt nazw? Nie, kod taki będzie poprawny. W obiektach klasy
Pochodna będą znajdować się dwa pola (Bazowa::x) i (Pochodna::x).
#include <iostream>
using namespace std;
class Bazowa
{
public:
int x;
};
class Pochodna : public Bazowa
{
public:
int x;
};
50
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II.
______________________________________________________________________
______________________________________________________________________
©2006 Jerzy Kluczewski
Odwołanie się do pola x bezpośrednie (np.
obiekt.x = 2;
) spowoduje przesłonięcie
pola z klasy bazowej Bazowa. Nie ma więc do niej bezpośredniego dostępu. Do tego
pola x, które jest dziedziczone z klasy bazowej Bazowa mamy dostęp poprzez operator
zakresu ::. (np.
obiekt.Bazowa::x = 1;
).
XIV.
Prosta hierarchia klas
Dziedziczenie pozwala na budowanie hierarchii klas czyli na wyprowadzanie z jednej
klasy bazowej wielu klas pochodnych, np. z ogólnej klasy Pojazd mogłyby dziedziczyć
takie klasy, jak Rower, Pociąg, Samochód. W przykładzie pokazano prostą hierarchię
dziedziczenia klas A,B,C oraz zachowanie się ich konstruktorów.
int main()
{
Pochodna obiekt;
obiekt.x = 2;
obiekt.Bazowa::x = 1;
cout << obiekt.x << endl;
cout << obiekt.Bazowa::x << endl;
system("pause");
return 0;
}
class A
class B
class C
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II. 51
______________________________________________________________________
©2006 Jerzy Kluczewski
Przykładowy program klasy2Unit1.cpp
#include <iostream>
using namespace std;
class A
{
public:
int x;
A() {cout<<"Konstruktor klasy A"<<endl;}
};
class B : public A
{
public:
int y;
B() {cout<<"Konstruktor klasy B"<<endl;}
};
class C : public B
{
public:
int z;
C() {cout<<"Konstruktor klasy C"<<endl;}
};
int main()
{
C obiekt;
obiekt.x = 10;
obiekt.y = 20;
obiekt.z = 30;
cout << obiekt.x << endl;
cout << obiekt.y << endl;
cout << obiekt.z << endl;
system("pause");
return 0;
}
52
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II.
______________________________________________________________________
______________________________________________________________________
©2006 Jerzy Kluczewski
Instrukcja
spowoduje utworzenie obiektu klasy C, która jest pochodną w stosunku do klasy
nadrzędnej B, z kolei klasa B jest pochodną klasy bazowej A. Spowoduje to, że
automatycznie zostaną wywołane konstruktory domyślne (jeśli takie zostały
zdefiniowane):
Konstruktor klasy A
Konstruktor klasy B
Konstruktor klasy C
XV.
Literatura pomocnicza
A. Majczak. C++ Przykłady praktyczne. Wydawnictwo Mikom, Warszawa 2003.
J. Liberty. C++ dla każdego. Wydawnictwo Helion, Gliwice 2002.
K. Loudon. C++ Leksykon kieszonkowy. Wydawnictwo Helion, Gliwice 2003.
A. Stasiewicz. C++ Ćwiczenia praktyczne. Wydawnictwo Helion, Gliwice 2004.
C obiekt;
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II. 53
______________________________________________________________________
©2006 Jerzy Kluczewski
XVI.
Odpowiedzi do zadań
Zadanie VII-1
Zadanie1Unit1.cpp
// Program Zadanie1Unit1.cpp
// Odpowiedź do Zadania VII-1
#include <iostream>
using namespace std;
class Example
{
public:
int* pole1;
Example()
{
pole1 = new int;
*pole1 = 0;
cout <<"Wywolanie konstruktora"<<endl;
}
~Example()
{
delete pole1;
cout <<"Wywolanie destruktora"<<endl;
}
};
int main()
{
Example example;
*(example.pole1) = 100;
cout << *(example.pole1) << endl;
system("pause");
return 0;
}
54
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II.
______________________________________________________________________
______________________________________________________________________
©2006 Jerzy Kluczewski
Zadanie VII-2
Zadanie2Unit1.cpp
// Program Zadanie2Unit1.cpp
// Odpowiedź do Zadania VII-2
#include <iostream>
using namespace std;
class Example
{
public:
int* pole1;
Example()
{
pole1 = new int;
*pole1 = 0;
cout <<"Wywolanie konstruktora"<<endl;
}
~Example()
{
delete pole1;
cout <<"Wywolanie destruktora"<<endl;
}
};
int main()
{
Example *example = new Example();
*(example->pole1) = 100;
cout << *(example->pole1) << endl;
delete example;
system("pause");
return 0;
}
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II. 55
______________________________________________________________________
©2006 Jerzy Kluczewski
Zadanie VII-3
Zadanie3.h
Zadanie3Unit1.cpp
class TPunkt
{
public:
int *x;
int *y;
TPunkt(int x, int y);
~TPunkt();
int pobierzX();
int pobierzY();
void ustawX(int wspX);
void ustawY(int wspY);
void ustawWspolrzedne(TPunkt &punkt);
void ustawWspolrzedne(int wspX, int wspY);
};
#include <iostream>
#include "Zadanie3.h"
using namespace std;
TPunkt::TPunkt(int x = 0, int y = 0) {
this->x = new int(x);
this->y = new int(y);
}
TPunkt ::~TPunkt() {
delete x;
delete y;
}
int TPunkt::pobierzX() {
return *x;
}
int TPunkt::pobierzY() {
return *y;
}
void TPunkt::ustawX(int wspX) {
*x = wspX;
}
void TPunkt::ustawY(int wspY) {
*y = wspY;
}
56
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II.
______________________________________________________________________
______________________________________________________________________
©2006 Jerzy Kluczewski
Zadanie3Unit1.cpp - ciąg dalszy
Zadanie VII-4
Zadanie4.h
class TKontener
{
public:
TKontener(int size);
~TKontener();
int* tab;
int size;
int get(int index);
int set(int index, int value);
};
void TPunkt::ustawWspolrzedne(TPunkt &punkt)
{
*x = *punkt.x;
*y = *punkt.y;
}
void TPunkt::ustawWspolrzedne(int wspX, int wspY)
{
*x = wspX;
*y = wspY;
}
int main()
{
TPunkt punkt(100, 200);
cout << punkt.pobierzX() << endl;
cout << punkt.pobierzY() << endl;
system("pause");
return 0;
}
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II. 57
______________________________________________________________________
©2006 Jerzy Kluczewski
Zadanie4Unit1.cpp
#include <iostream>
#include "Zadanie4.h"
using namespace std;
TKontener::TKontener(int size) {
tab = new int[size];
this->size = size;
}
TKontener::~TKontener() {
delete tab;
}
int TKontener::get(int index) {
if (index<0 || index > size-1)
return -1;
return tab[index];
}
int TKontener::set(int index, int value) {
if (index<0 || index > size-1)
{
int* newtab = new int[index*2];
for(int i=0; i< size; i++)
newtab[i] = tab[i];
delete tab;
tab = newtab;
size = index * 2;
}
tab[index] = value;
return 0;
}
int main()
{
TKontener kontener(5);
for (int i=0; i < 5; i++)
kontener.set(i,10);
kontener.set(7,1);
kontener.set(8,2);
for (int i=0; i < 9; i++)
cout << kontener.get(i) << endl;
system("pause");
return 0;
}
58
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II.
______________________________________________________________________
______________________________________________________________________
©2006 Jerzy Kluczewski
Zadanie IX-1
Zadanie5Unit1.h
Zadanie5Unit1.cpp
class TOsoba
{
public:
char* imie;
char* nazwisko;
TOsoba(char* imie, char* nazwisko); //konstruktor
TOsoba(TOsoba &osoba); //konstruktor kopiujący
~TOsoba(); // destruktor
};
#include <iostream>
#include "Zadanie5Unit1.h"
using namespace std;
TOsoba::TOsoba(char* imie, char* nazwisko)
{
int dlugosc = strlen(imie);
this->imie = new char[dlugosc+1];
strcpy(this->imie, imie);
dlugosc = strlen(nazwisko);
this->nazwisko = new char[dlugosc+1];
strcpy(this->nazwisko, nazwisko);
}
TOsoba::TOsoba(TOsoba &osoba)
{
int dlugosc = strlen(osoba.imie);
this->imie = new char[dlugosc+1];
strcpy(this->imie, osoba.imie);
dlugosc = strlen(osoba.nazwisko);
this->nazwisko = new char[dlugosc+1];
strcpy(this->nazwisko, osoba.nazwisko);
}
TOsoba::~TOsoba()
{
delete this->imie;
delete this->nazwisko;
}
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II. 59
______________________________________________________________________
©2006 Jerzy Kluczewski
Zadanie IX-2
Zadanie6Unit1.h
Zadanie6Unit1.cpp
int main()
{
TOsoba osoba1("Adam", "Abacki");
cout<<"Dane obiektu osoba1:"<<endl;
cout<<osoba1.imie<<" "<<osoba1.nazwisko<<endl;
TOsoba osoba2(osoba1);
cout<<"Dane obiektu osoba2:"<<endl;
cout<<osoba2.imie<<" "<<osoba2.nazwisko<<endl;
system("pause");
return 0;
}
class TNapis
{
public:
char* dane;
TNapis(); //konstruktor
int Dlugosc(void);
TNapis(char* tekst); //konstruktor
TNapis(TNapis &napis); //konstruktor kopiujący
~TNapis(); // destruktor
};
#include <iostream>
#include "Zadanie6Unit1.h"
using namespace std;
TNapis::TNapis()
{
dane = new char[1];
dane[0] = '\0';
}
60
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II.
______________________________________________________________________
______________________________________________________________________
©2006 Jerzy Kluczewski
int TNapis::Dlugosc(void)
{
return strlen(this->dane);
}
TNapis::TNapis(char* tekst)
{
int rozmiar = strlen(tekst) + 1;
dane = new char[rozmiar];
strcpy(this->dane, tekst);
}
TNapis::TNapis(TNapis &napis)
{
dane = new char[napis.Dlugosc()+1];
strcpy(this->dane,napis.dane);
}
TNapis::~TNapis()
{
delete this->dane;
}
int main()
{
TNapis s1;
TNapis s2("ABC");
TNapis s3(s2);
cout<<"Napis "<<s1.dane<< " ma dlugosc "<<s1.Dlugosc()<<endl;
cout<<"Napis "<<s2.dane<< " ma dlugosc "<<s2.Dlugosc()<<endl;
cout<<"Napis "<<s3.dane<< " ma dlugosc "<<s3.Dlugosc()<<endl;
system("pause");
return 0;
}
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II. 61
______________________________________________________________________
©2006 Jerzy Kluczewski
Zadanie IX-3
Zadanie7Unit1.h
Zadanie7Unit1.cpp
class TKlasa
{
public:
static int count;
TKlasa(); //konstruktor
~TKlasa(); // destruktor
};
#include <iostream>
#include "Zadanie7Unit1.h"
using namespace std;
int TKlasa::count = 0;
TKlasa::TKlasa()
{
count++;
}
TKlasa::~TKlasa()
{
count– –;
}
62
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II.
______________________________________________________________________
______________________________________________________________________
©2006 Jerzy Kluczewski
Zadanie XII-1
PracownikUnit1.h
PracownikUnit1.cpp
//klasa bazowa
class TOsoba
{
public:
char* imie;
char* nazwisko;
TOsoba(); //konstruktor domyslny
TOsoba(char* imie, char* nazwisko); //konstruktor
TOsoba(TOsoba &osoba); //konstruktor kopiujący
~TOsoba(); // destruktor
};
//klasa pochodna
class TPracownik : public TOsoba
{
public:
char* stanowisko;
TPracownik(char* imie, char* nazwisko, char* stanowisko);
//konstruktor
TPracownik(TPracownik &pracownik); //konstruktor kopiujący
~TPracownik(); // destruktor
};
#include <iostream>
#include "PracownikUnit1.h"
using namespace std;
//klasa bazowa
TOsoba::TOsoba()
{
cout << "Konstruktor domyslny TOsoba" << endl;
this->imie = new char[1];
strcpy(this->imie, "");
this->nazwisko = new char[1];
strcpy(this->nazwisko, "");
}
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II. 63
______________________________________________________________________
©2006 Jerzy Kluczewski
PracownikUnit1.cpp – ciąg dalszy
TOsoba::TOsoba(char* imie, char* nazwisko)
{
int dlugosc = strlen(imie);
this->imie = new char[dlugosc+1];
strcpy(this->imie, imie);
dlugosc = strlen(nazwisko);
this->nazwisko = new char[dlugosc+1];
strcpy(this->nazwisko, nazwisko);
}
TOsoba::TOsoba(TOsoba &osoba)
{
int dlugosc = strlen(osoba.imie);
this->imie = new char[dlugosc+1];
strcpy(this->imie, osoba.imie);
dlugosc = strlen(osoba.nazwisko);
this->nazwisko = new char[dlugosc+1];
strcpy(this->nazwisko, osoba.nazwisko);
}
TOsoba::~TOsoba()
{
cout << "Destruktor TOsoba"<<endl;
delete this->imie;
delete this->nazwisko;
}
//klasa pochodna
TPracownik::TPracownik(char* imie, char* nazwisko, char* stanowisko)
{
strcpy(this->imie, imie);
strcpy(this->nazwisko, nazwisko);
int dlugosc = strlen(stanowisko);
this->stanowisko = new char[dlugosc+1];
strcpy(this->stanowisko, stanowisko);
}
TPracownik::TPracownik(TPracownik &pracownik)
{
int dlugosc = strlen(pracownik.stanowisko);
this->stanowisko = new char[dlugosc+1];
strcpy(this->stanowisko, pracownik.stanowisko);
}
64
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II.
______________________________________________________________________
______________________________________________________________________
©2006 Jerzy Kluczewski
PracownikUnit1.cpp – ciąg dalszy
TPracownik::~TPracownik()
{
cout << "Destruktor TPracownik"<<endl;
delete this->stanowisko;
}
int main()
{
TPracownik pracownik1("Barnaba", "Celinowski", "Kierownik");
cout<<"Dane obiektu pracownik1:"<<endl;
cout<<pracownik1.imie<<endl;
cout<<pracownik1.nazwisko<<endl;
cout<<pracownik1.stanowisko<<endl;
system("pause");
return 0;
}
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II. 65
______________________________________________________________________
©2006 Jerzy Kluczewski
SPIS TREŚCI
I.
Przestrzenie nazw ..................................................................................................... 2
Przestrzeń nazw std....................................................................................................... 5
II. Obiekty...................................................................................................................... 6
Projekt obiektu .............................................................................................................. 6
III.
Klasa ..................................................................................................................... 7
Klasa TPunkt................................................................................................................. 8
Pola klasy...................................................................................................................... 8
Metody klasy................................................................................................................. 8
Określenie struktury klasy ............................................................................................ 8
Odwołania do składowych obiektu............................................................................. 11
Wiele obiektów tej samej klasy .................................................................................. 11
IV.
Właściwości obiektu ........................................................................................... 12
Argumenty metod ....................................................................................................... 12
Odwołanie this ............................................................................................................ 14
V. Przeciążanie metod ................................................................................................. 15
Obiekt jako argument.................................................................................................. 15
Overloading ................................................................................................................ 17
VI.
Sposoby przekazywania argumentów................................................................. 19
VII.
Dynamiczne obiekty ........................................................................................... 21
Inicjalizacja pól........................................................................................................... 22
Konstruktor ................................................................................................................. 23
Destruktor ................................................................................................................... 24
Zadanie VII-1.......................................................................................................... 25
Zadanie VII-2.......................................................................................................... 25
Zadanie VII-3.......................................................................................................... 26
Zadanie VII-4.......................................................................................................... 26
VIII. Konstruktory ....................................................................................................... 26
Argumenty konstruktorów.......................................................................................... 26
Konstruktor kopiujący ................................................................................................ 28
Konstruktor kopiujący i pola wskaźnikowe................................................................ 29
Zadanie VIII-1 ........................................................................................................ 29
IX.
Składowe statyczne............................................................................................. 33
Pola statyczne ............................................................................................................. 33
Metody statyczne ........................................................................................................ 35
Dostęp do pól i metod ................................................................................................. 35
Dostęp do pól i metod – c.d. ....................................................................................... 37
Zadanie IX-1 ........................................................................................................... 38
Zadanie IX-2 ........................................................................................................... 38
Zadanie IX-3 ........................................................................................................... 38
X. Dziedziczenie.......................................................................................................... 39
XI.
Dostęp do składowych klasy............................................................................... 44
Składowe publiczne .................................................................................................... 44
Składowe prywatne..................................................................................................... 44
Składowe chronione.................................................................................................... 44
XII.
Modyfikatory i dziedziczenie ............................................................................. 46
Praktyczny cel stosowania modyfikatorów ............................................................ 47
Reguły budowania klas potomnych ........................................................................ 48
Zadanie XII-1.......................................................................................................... 49
66
C++. Programowanie obiektowe. Ćwiczenia podstawowe. Cz. II.
______________________________________________________________________
______________________________________________________________________
©2006 Jerzy Kluczewski
XIII. Przesłanianie składowych klasy.......................................................................... 49
XIV.
Prosta hierarchia klas ...................................................................................... 50
XV.
Literatura pomocnicza ........................................................................................ 52
XVI.
Odpowiedzi do zadań...................................................................................... 53
Zadanie VII-1.......................................................................................................... 53
Zadanie VII-2.......................................................................................................... 54
Zadanie VII-3.......................................................................................................... 55
Zadanie VII-4.......................................................................................................... 56
Zadanie IX-1 ........................................................................................................... 58
Zadanie IX-2 ........................................................................................................... 59
Zadanie IX-3 ........................................................................................................... 61
Zadanie XII-1.......................................................................................................... 62