[ Pobierz całość w formacie PDF ]
//brak dostępu do m_a bo jest to prywatny atrybut klasy bazowej A.
//brak dostępu do m_b bo jest to zabezpieczony atrybut klasy bazowej A.
//brak dostępu do m_d bo jest to prywatny atrybut klasy B.
//brak dostępu do m_e bo jest to zabezpieczony atrybut klasy B.
printf("c=(?,?,%d,?,?,%d)\n",c.m_c,c.m_f);
return 0;
}
W tym przykładzie są zadeklarowane trzy klasy. Klasą bazową jest klasa A. Z niej
powstaje klasa B przez dziedziczenie z ukrytą klasą bazową, oraz klasa C przez dziedzi-
czenie z jawną klasą bazową. Obiekty klasy A zajmują 12 bajtów w pamięci komputera
(trzy liczby całkowite: m a, m b, m c). Obiekty klasy B i C zajmują 24 bajty w pamięci
komputera (sześć liczb całkowitych: m a, m b, m c, m d, m e, m f).
Jak widać sposób dziedziczenia nie ma wpływu na dostęp metod pochodnych do
atrybutów klasy bazowej. To co było prywatne jest niedostępne a to co było zabezpieczone
i publiczne jest dostępne. Natomiast jest wpływ na dostęp z zewnątrz: to co było publiczne
w klasie bazowej (dostępne) przestaje być dostępne przy dziedziczeniu z ukrytą klasą
bazową
Aby lepiej zrozumieć potrzebę dziedziczenie proponuję zdefiniować klasy, które bedą
potrzebne do napisania (w bliżej nieokreślonej przyszłości) programu graficznego do two-
rzenia dwuwymiarowych rysunków. Ten program będzie przechowywał obiekty widoczne
na rysunku jako figury geometryczne (np. odcinki, linie łamane, prostokąty, wielokąty,
koła, elipsy itp.). Każda figura będzie znała swoje położenie w umownych jednostkach
(np. w milimetrach względem umownego początku współrzędnych). Każda z tych figur
będzie potrafiła się narysować4. Proponuję zacząć od trzech najprostszych figur:
Odcinek: opisany przez wspołrzędne początku i współrzędne końca.
Prostokąt: opisany przez wspołrzędne środka i dwie długość boków.
Elipsa: opisana przez wspołrzędne środka i dwa promienie.
W przyszłości można wyposażyć te figury w dodatkowe atrybuty: kolor, grubość linii,
sposób wypełnienia itp. Jak widać, we wszystkich figurach powtarzają się współrzędne
4
W miarę rozwoju programu będzie można dopisywać nowe cechy figur
47
początku (dwie liczby rzeczywiste). Wspólne atrybuty umieszczamy w klasie bazowej.
class CShape
{
protected:
float m_x;
float m_y;
public:
CShape(float x, float y) { m_x=x; m_y=y; }
void Draw() {}
};
Przewiduję, że metody obiektu potomnego będą miały prawo odwołać się bezpośred-
nio do współrzędnych początku dlatego zostały umieszczone w sekcji protected. Metoda
Draw() nic nie wykonuje bo nie można narysować punktu o zerowym rozmiarze. Na bazie
klasy CShape budujemy klasy pochodne czyli:
class CLine:public CShape
{
float m_x2;
float m_y2;
public:
CLine(float x, float y, float x2, float y2) : CShape(x,y)
{
m_x2=x2;
m_y2=y2;
}
void Draw()
{
printf("Rysuję odcinek od punktu (%f, %f) do punktu (%f, %f)\n",
m_x,m_y,m_x2,m_y2);
}
};
class CRectangle:public CShape
{
float m_dx;
float m_dy;
public:
CRectangle(float x, float y, float dx, float dy) : CShape(x,y)
{
m_dx=dx;
m_dy=dy;
}
void Draw()
{
printf("Rysuję prostokąt o środku (%f, %f) i wielkości %f, %f\n",
m_x,m_y,m_dx,m_dy);
}
};
class CEllipse:public CShape
{
float m_rx;
float m_ry;
public:
48
CEllipse(float x, float y, float rx, float ry) : CShape(x,y)
{
m_rx=rx;
m_ry=ry;
}
void Draw()
{
printf("Rysuję elipsę o środku (%f, %f) i promieniach (%f, %f)\n",
m_x,m_y,m_rx,m_ry);
}
};
Na uwagę zasługuje sposób wyłania konstruktora klasy bazowej. Po nagłówku a przed
ciałem funkcji dajem znak : (dwukropek) a po nim nazwę konstrukora klasy bazowej
i listę parametrów aktualnych. W celu przetestowania klas można napisać główną proce-
durę, która zadeklaruje trzy zmienne o klasach CLine, CRectangle, CEllipse i wywoła
dla nich metody Draw().
#pragma argsused
int main(int argc, char **argv)
{
CLine line(10,10,30,40);
CRectangle rectangle(50,50,25,35);
CEllipse ellipse(40,40,10,20);
line.Draw();
rectangle.Draw();
ellipse.Draw();
return 0;
}
Każdy z tych obiektów (CLine, CRectangle, CEllipse) będzie zajmował w pamięcie
komputera 16 bajtów (cztery liczby typu float). Kompilator wie, którą metodę Draw()
wywołać, gdyż znane są typy zmiennych. Na przykład zmienna line jest typu CLine
czyli dla tej zmiennej zostanie wywołana metoda CLine::Draw().
Wersja z obiektami dynamicznymi niczego tu nie zmieni.
#pragma argsused
int main(int argc, char **argv)
{
CLine *line;
CRectangle *rectangle;
CEllipse *ellipse;
line=new CLine(10,10,30,40);
rectangle=new CRectangle(50,50,25,35);
ellipse=new CEllipse(40,40,10,20);
line->Draw();
rectangle->Draw();
ellipse->Draw();
delete line;
delete rectangle;
49
delete ellipse;
return 0;
}
15.5 Polimorfizm
Obiekty graficzne (owe figury) będą występować na rysunku wielokrotnie. Można oczy-
wiście zdefiniować wiele tablic, po jednej na każdy typ figury, ale takie rozwiązanie będzie
bardzo niewygodne dla programisty. Każdą operację na figurach (np. rysowanie, szukanie
prostokąta otaczającego wszystkie figury, skalowanie figur, poszukiwanie figur widocznych
na podanym obszrze itp.) trzeba będzie wykonywać na każdej tablicy osobno. Programo-
wanie obiektowe daje możliwość zadeklarowania jednej tablicy, która przechowuje adresy
obiektów bazowych.
Gdyby zmienić program główny tak jak pokazano niżej:
#pragma argsused
int main(int argc, char **argv)
{
CShape *figury[10];
int i;
figury[0]=new CLine(10,10,30,40);
figury[1]=new CRectangle(50,50,25,35);
figury[2]=new CEllipse(40,40,10,20);
for (i=0; i
figury[i]->Draw();
for (i=0; i
delete figury[i];
return 0;
}
to program byłby błędny gdyż kompilator dla zmiennych figury[i] wywoła metodę
CShape::Draw(). Niezwykłe jest to, że programowanie obiektowe zezwala na podsta-
wienie adresu obiektu potomnego od zmiennej wskazującej na obiek klasy bazowej. Aby
[ Pobierz całość w formacie PDF ]