Lekcja 28LEKCJA 28
Składowe prywatne a klasy i funkcje zaprzyjaźnione
Jak już wiesz, programy mają dostęp do prywatnych składowych klasy jedynie za
pośrednictwem publicznych metod tej klasy. Definiując jak najwięcej prywatnych
składowych klasy zmniejszasz możliwość błędnego użycia wartości składowych w
programie, ponieważ program ma do nich dostęp tylko przy użyciu funkcji
interfejsu (które mogą kontrolować dostęp do składowej). Jednak w niektórych
programach możesz wiele zyskać dając jednej klasie bezpośredni dostęp do
prywatnych składowych innej klasy. Dzięki temu program nie będzie musiał
wywoływać funkcji interfejsu. W takich sytuacjach możesz zdefiniować dla klasy
klasę zaprzyjaźnioną (friend), która będzie miała dostęp do jej prywatnych
składowych. Podczas tej lekcji przedstawimy, w jaki sposób możesz określać w
programach klasy zaprzyjaźnione. Pod koniec tej lekcji będziesz rozumiał
następujące zagadnienia podstawowe:
Przy użyciu słowa kluczowego friend jedna klasa może informować C++ o swoich
klasach zaprzyjaźnionych, czyli klasach, które mają bezpośredni dostęp do
prywatnych składowych danej klasy.
prywatne składowe klasy mają chronić dane klasy i w związku z tym powinieneś
nadawać status zaprzyjaźnionych tylko tym klasom, które rzeczywiście muszą
mieć bezpośredni dostęp do prywatnych składowych klasy.
W C++ możesz ograniczyć dostęp klasy zaprzyjaźnionej tylko do określonego
zbioru funkcji.
Dzięki składowym prywatnym możesz chronić klasy i zmniejszać możliwość błędów.
Powinieneś zatem ograniczyć maksymalnie korzystanie z klas zaprzyjaźnionych.
Zawsze, gdy program może bezpośrednio modyfikować składowe klas, jest możliwość
błędów.
Definiowanie klasy zaprzyjaźnionej
C++ daje klasie zaprzyjaźnionej z daną klasą dostęp do jej prywatnych
składowych. Aby poinformować C++, że pewna klasa jest zaprzyjaźniona z daną
klasą, piszesz w definicji klasy słowo kluczowe friend i podajesz nazwę klasy
zaprzyjaźnionej. Na przykład następująca klasa ksiazka mówi, że klasa
bibliotekarz jest z nią zaprzyjaźniona. W związku z tym obiekty klasy
bibliotekarz mają bezpośredni dostęp do prywatnych składowych klasy ksiazka przy
użyciu operatora wyboru składowej (kropki):
class ksiazka {
pyblic:
ksiazka (char *, char *, char *);
void inf_o_ksiazce;
friend bibliotekarz;
private:
char tytul [64];
char autor [64];
char skrot [64];
};
Jak widzimy, podanie klasy zaprzyjaźnionej wymaga tylko jednej instrukcji
wstawianej do definicji klasy. Na przykład w przedstawionym poniżej programie
KSIAZKI.CPP klasa bibliotekarz jest klasą zaprzyjaźnioną dla klasy ksiazka. W
związku z tym funkcje klasy bibliotekarz mają bezpośredni dostęp do prywatnych
składowych klasy ksiazka. W tym przykładzie program przy użyciu funkcji
zmien_skrot_katalogowy zmienia skrót katalogowy dla podanej książki:
#include
#include
class ksiazka {
pyblic:
ksiazka (char *, char *, char *);
void inf_o_ksiazce;
friend bibliotekarz;
private:
char tytul [64];
char autor [64];
char skrot [64];
};
ksiazka::ksiazka (char *tytul, char *autor, char *skrot)
{
strcpy (ksiazka::tytul, tytul);
strcpy (ksiazka::autor, autor);
strcpy (ksiazka::skrot, skrot);
}
void ksiazka::inf_o_ksiazce (void)
{
cout << "Tytuł: " << tytul << endl;
cout << "Autor: " << autor << endl;
cout << "Skrót katalogowy: " << skrot << endl;
}
class bibliotekarz {
public:
void zmien_skrot_katalogowy (ksiazka *, char *);
char *skrot_katalogowy (ksiazka);
};
void bibliotekarz::zmien_skrot_katalogowy (ksiazka *ta_ksiazka,
char *nowy_skrot)
{
strcpy (ta_ksiazka->skrot, nowy_skrot);
}
char *bibliotekarz::skrot_katalogowy (ksiazka ta_ksiazka)
{
static char skrot[64];
strcpy (skrot, ta_ksiazka.skrot);
return (skrot);
}
void main(void)
{
ksiazka programowanie ("Wygraj z C++", "Jamsa", "P101");
bibliotekarz biblioteka;
programowanie.inf_o_ksiazce();
biblioteka.zmien_skrot_katalogowy (&programowanie, "ŁATWE C++ 101");
programowanie.inf_o_ksiazce();
}
Jak widzimy, program przekazuje funkcji zmien_skrot_katalogowy klasy
bibliotekarz adres obiektu typu ksiazka. Ponieważ funkcja ma zmienić składową
klasy, program musi przekazywać jej adres obiektu. Funkcja realizuje dostęp do
składowej przy użyciu wskaźnika. Zrób eksperyment i usuń linię zawierającą słowo
kluczowe friend z definicji klasy ksiazka. Ponieważ klasa bibliotekarz straci w
ten sposób prawo dostępu do prywatnych składowych klasy ksiazka, kompilator
języka C++ przy każdym odwołaniu do prywatnych danych klasy ksiazka
zasygnalizuje błędy syntaktyczne.
Znaczenie klas zaprzyjaźnionych
Zwykle jedynym sposobem dostępu w programie do prywatnych składowych klasy jest
wywołanie funkcji interfejsu. W niektórych programach będzie wygodniej (lub
bardziej efektywnie z uwagi na szybkość działania), gdy przyznasz pewnej klasie
dostęp do prywatnych składowych innej klasy. Aby to zrealizować, musisz
poinformować kompilator języka C++, że klasa, której dajesz taki dostęp, jest
zaprzyjaźniona (friend). A kompilator pozwoli klasie zaprzyjaźnionej korzystać z
prywatnych składowych danej klasy. Aby zdefiniować klasę jako zaprzyjaźnioną,
napisz w części public: definicji klasy, dla której podajesz klasę
zaprzyjaźnioną, słowo kluczowe friend i nazwę klasy zaprzyjaźnionej, np.:
class Flip {
public:
friend Flap;
// Inne składowe
private:
// Prywatne składowe
};
Czym klasa zaprzyjaźniona różni się od składowych chronionych
Wiesz już, że możesz w C++ zdefiniować składowe chronione, do których mają
bezpośredni dostęp tylko klasy pochodne danej klasy, a które poza tym są
traktowane tak jak składowe prywatne. Pamiętaj, że jedynymi klasami, które mają
dostęp do chronionych składowych danej klasy, są jej klasy pochodne, czyli
innymi słowy, klasy dziedziczące składowe klasy bazowej. Natomiast klasy
zaprzyjaźnione języka C++ nie są zwykle związane z daną klasą (nie ma między
nimi dziedziczenia). Jedynym sposobem na to, by takie niezwiązane klasy miały
dostęp do prywatnych składowych danej klasy jest umieszczenie w definicji tej
klasy informacji dla kompilatora, że druga klasa jest z nią zaprzyjaźniona
(friend).
Ograniczanie dostępu klasy zaprzyjaźnionej
Jak właśnie się dowiedziałeś, gdy określisz jedną klasę jako zaprzyjaźnioną z
drugą klasą, to klasa zaprzyjaźniona ma dostęp do prywatnych składowych drugiej
klasy. Jak już mówiliśmy, im szerszy dostęp dasz do prywatnych składowych klasy,
tym większe będzie niebezpieczeństwo wystąpienia błędów. Zatem, jeśli tylko
niektóre funkcje klasy zaprzyjaźnionej powinny mieć dostęp do prywatnych
składowych danej klasy, to możesz przyznać dostęp do nich tylko wybranym
funkcjom klasy zaprzyjaźnionej. Załóżmy, na przykład, że klasa bibliotekarz z
poprzedniego programu ma wiele różnych funkcji. Ponadto przyjmijmy, że tylko
funkcjom zmien_skrot_katalogowy i skrot_katalogowy jest potrzebny dostęp do
prywatnych składowych klasy ksiazka. W definicji klasy ksiazka możesz ograniczyć
dostęp do jej prywatnych składowych tylko do tych dwóch funkcji, czyli
otrzymamy:
class ksiazka {
pyblic:
ksiazka (char *, char *, char *);
void inf_o_ksiazce;
friend char *bibliotekarz::skrot_katalogowy (ksiazka);
friend void bibliotekarz::zmien_skrot_katalogowy (ksiazka, char *);
private:
char tytul [64];
char autor [64];
char skrot [64];
};
Jak widzimy, instrukcje friend zawierają pełne prototypy tych funkcji klasy
bibliotekarz, które mają bezpośredni dostęP do prywatnych składowych klasy
ksiazka.
Znaczenie funkcji zaprzyjaźnionych
Gdy przyznajesz w swoim programie dostęp do prywatnych składowych klasy
wprowadzając klasę zaprzyjaźnioną, to możesz ograniczyć liczbę funkcji
składowych klasy zaprzyjaźnionej, które będą miały taki dostęp przy użyciu
funkcji zaprzyjaźnionych. Aby zadeklarować funkcję zaprzyjaźnioną, należy podać
słowo kluczowe friend, a za nim pełny prototyp funkcji, np.
public:
friend nazwa_klasy::nazwa_funkcji (typy_parametrow);
Tylko określone w ten sposób funkcje będą miały dostęp do prywatnych składowych
klasy poprzez operator wyboru składowej.
Gdy twój program w jednej klasie odwołuje się do innej klasy, to mogą się
zdarzyć błędy syntaktyczne, jeśli kolejność definicji klas nie jest odpowiednia.
W naszym przykładzie w definicji klasy ksiazka są wykorzystywane prototypy
funkcji zdefiniowanych w klasie bibliotekarz. Zatem definicja klasy bibliotekarz
musi być przed definicją klasy ksiazka. Ale jeśli zajrzysz do definicji klasy
bibliotekarz, to zauważysz, że odwołuje się ona do klasy ksiazka:
class bibliotekarz {
public:
void zmien_skrot_katalogowy (ksiazka *, char *);
char *skrot_katalogowy (ksiazka);
};
Ponieważ nie można jednocześnie umieścić definicji klasy bibliotekarz przed
definicją klasy ksiazka i definicji klasy ksiazka przed definicją klasy
bibliotekarz, możesz w C++ przy użyciu jednej linii kodu poinformować kompilator
języka C++, że ksiazka jest klasą, która zostanie zdefiniowana później w
następujący sposób:
class ksiazka;
Przedstawiony poniżej program OGR_FRI.CPP przy użyciu funkcji zaprzyjaźnionych
ogranicza dostęp klasy bibliotekarz do prywatnych danych klasy ksiazka:
#include
#include
class ksiazka;
class bibliotekarz {
public:
void zmien_skrot_katalogowy (ksiazka *, char *);
char *skrot_katalogowy (ksiazka);
};
class ksiazka {
pyblic:
ksiazka (char *, char *, char *);
void inf_o_ksiazce;
friend char *bibliotekarz::skrot_katalogowy (ksiazka);
friend void bibliotekarz::zmien_skrot_katalogowy (ksiazka, char *);
private:
char tytul [64];
char autor [64];
char skrot [64];
};
ksiazka::ksiazka (char *tytul, char *autor, char *skrot)
{
strcpy (ksiazka::tytul, tytul);
strcpy (ksiazka::autor, autor);
strcpy (ksiazka::skrot, skrot);
}
void ksiazka::inf_o_ksiazce (void)
{
cout << "Tytuł: " << tytul << endl;
cout << "Autor: " << autor << endl;
cout << "Skrót katalogowy: " << skrot << endl;
}
void bibliotekarz::zmien_skrot_katalogowy (ksiazka *ta_ksiazka,
char *nowy_skrot)
{
strcpy (ta_ksiazka->skrot, nowy_skrot);
}
char *bibliotekarz::skrot_katalogowy (ksiazka ta_ksiazka)
{
static char skrot[64];
strcpy (skrot, ta_ksiazka.skrot);
return (skrot);
}
void main(void)
{
ksiazka programowanie ("Wygraj z C++", "Jamsa", "P101");
bibliotekarz biblioteka;
programowanie.inf_o_ksiazce();
biblioteka.zmien_skrot_katalogowy (&programowanie, "ŁATWE C++ 101");
programowanie.inf_o_ksiazce();
}
Jak widzimy, program najpierw przy użyciu jednej linii kodu informuje
kompilator, że dalej zostanie zdefiniowana klasa ksiazka. Ponieważ dzięki temu
kompilator wie, że będzie utworzona klasa ksiazka, to w definicji klasy
bibliotekarz można się odwoływać do klasy ksiazka, która zostanie zdefiniowana
dopiero później.
Wprowadzanie nazwy klasy
Identyfikator to nazwa, na przykład nazwa zmiennej czy typu. Jeśli twój program
używa klas zaprzyjaźnionych, to może się zdarzyć tak, że jedna definicja klasy
odwołuje się do (nazwy lub identyfikatora) drugiej klasy, o której kompilator
języka C++ jeszcze nie wie. W takich sytuacjach kompilator będzie sygnalizować
błędy syntaktyczne. Aby uniknąć takich błędów, możesz wprowadzać na początku
programu jedną linię informującą kompilator o identyfikatorze klasy:
class nazwa_klasy;
Zapamiętaj
Podczas tej lekcji dowiedziałeś się, w jaki sposób przy użyciu klas
zaprzyjaźnionych można przyznawać jednej klasie bezpośredni dostęp (poprzez
operator wyboru składowej) do prywatnych składowych innej klasy. Podczas lekcji
29 nauczysz się, w jaki sposób przy użyciu szablonów funkcji języka C++ można
upraszczać definiowanie podobnych funkcji. Zanim jednak przejdziemy do następnej
lekcji upewnij się, że opanowałeś już następujące zagadnienia podstawowe:
Dzięki klasom zaprzyjaźnionym możesz w programach dawać jednej klasie dostęp
do prywatnych składowych innej klasy poprzez operator wyboru składowej.
Aby zadeklarować jedną klasę jako zaprzyjaźnioną z drugą klasą, w definicji
drugiej klasy należy wpisać słowo kluczowe friend i nazwę klasy
zaprzyjaźnionej.
Po zadeklarowaniu jednej klasy jako zaprzyjaźnionej z drugą, wszystkie funkcje
składowe klasy zaprzyjaźnionej mają dostęp do prywatnych składowych drugiej
klasy.
Aby ograniczyć liczbę metod klasy zaprzyjaźnionej, które mają dostęp do
prywatnych składowych klasy, możesz deklarować funkcje zaprzyjaźnione. W tym
celu należy wpisać słowo kluczowe friend i nazwę funkcji, która ma mieć dostęp
do prywatnych składowych klasy.
Jeśli deklarujesz funkcje zaprzyjaźnione i kolejność definicji klas nie jest
odpowiednia, to dostaniesz komunikaty o błędach syntaktycznych. Jeśli chcesz
poinformować kompilator języka C++, że dany identyfikator jest nazwą klasy,
która l zostanie zdefiniowana później, to możesz umieścić w kodzie na początku
program u instrukcję typu class nazwa_klasy.
WsteczSpis treściDalej
Wyszukiwarka
Podobne podstrony:
Kris Jamsa Wygraj Z C lekcja32
Kris Jamsa Wygraj Z C lekcja 5
Kris Jamsa Wygraj Z C lekcja23
Kris Jamsa Wygraj Z C lekcja38
Kris Jamsa Wygraj Z C lekcja35
Kris Jamsa Wygraj Z C lekcja20
Kris Jamsa Wygraj Z C lekcja27
Kris Jamsa Wygraj Z C lekcja34
Kris Jamsa Wygraj Z C lekcja18
Kris Jamsa Wygraj Z C lekcja17
Kris Jamsa Wygraj Z C lekcja25
Kris Jamsa Wygraj Z C lekcja14
Kris Jamsa Wygraj Z C lekcja36
Kris Jamsa Wygraj Z C lekcja 4
Kris Jamsa Wygraj Z C lekcja33
Kris Jamsa Wygraj Z C lekcja 7
Kris Jamsa Wygraj Z C lekcja30
Kris Jamsa Wygraj Z C lekcja12
Kris Jamsa Wygraj Z C lekcja 6
więcej podobnych podstron