This post comes from the first version of this blog.
Please send me an email if anything needs an update. Thanks!
Dzisiaj czytałem nieco o klasach zaprzyjaźnionych w języku C++ [ang. friend classes] i podczas lektury jednego z artykułów przypomniało mi się znane z podstawówki powiedzenie “co je twoje, to je moje, a co moje, to nie rusz”. Przez chwilę patrzyłem na przykładowy kod i nagle wpadł mi do głowy pewien pomysł. Wymyśliłem implementację tego powiedzenia w C++. ;] Oznaczenie klasy jako zaprzyjaźnionej z inną oznacza m. in. to, że wszystkie atrybuty tej pierwszej, niezależnie od klasyfikatora dostępu [public, protected, private] stają się dostępne dla drugiej bez żadnych ograniczeń. Pierwsza klasa “przekazuje” wszystkie informacje o sobie tej drugiej tak, jakby kwalifikatorem dostępu dla wszystkich jej elementów [atrybutów i metod] było słowo kluczowe public. Analogia jest, przynajmniej dla mnie, bardzo wyraźna. ;]
Kod:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
/* File: friend.h */
#ifndef FRIEND$HEADER
#define FRIEND$HEADER
#include <iostream>
namespace FriendNS
{
class Me;
class You;
#include "me.h"
#include "you.h"
}
#endif
|
Plik friend.h to nagłówek dla całego projektu, zawierający wszystkie deklaracje i includy używanych klas i bibliotek C++.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
/* File: me.h */
#ifndef FRIEND$CLASS$ME
#define FRIEND$CLASS$ME
#include "friend.h"
class Me
{
private:
int myData;
/* public:
friend class You; */
public:
Me(void);
Me(int myData);
~Me(void);
// "native"
void setMyData(int myData);
int getMyData(void);
// available because of friendship declaration in "You" class
void setYourData(You *you, int yourData);
int getYourData(You *you);
};
#endif
|
W pliku me.h znajduje się deklaracja klasy Me, która będzie demonstrowała zastosowanie “przyjaźni” klas w praktyce.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
/* File: me.cpp */
#include "friend.h"
namespace FriendNS
{
Me::Me(void)
{
this->myData = 0;
}
Me::Me(int myData)
{
this->myData = myData;
}
Me::~Me(void)
{
// sorry ;]
}
void Me::setMyData(int myData)
{
this->myData = myData;
return;
}
int Me::getMyData(void)
{
return this->myData;
}
void Me::setYourData(You *you, int yourData)
{
you->yourData = yourData;
return;
}
int Me::getYourData(You *you)
{
return you->yourData;
}
}
|
Implementacja klasy Me, żeby nie było, że coś ukrywam ;]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
/* File: you.h */
#ifndef FRIEND$CLASS$YOU
#define FRIEND$CLASS$YOU
#include "friend.h"
class You
{
private:
int yourData;
public:
friend class Me;
public:
You(void);
You(int yourData);
~You(void);
// "native"
void setYourData(int yourData);
int getYourData(void);
// forbidden, because "Me" class has not declared friendship
/* void setMyData(Me *me, int myData);
int getMyData(Me *me); */
};
#endif
|
Deklaracja klasy You, która zawiera deklarację przyjaźni z klasą Me. Fragmenty w blokach komentarza zawierają analogiczną prezentację możliwości przyjaźni pomiędzy klasami którą można odblokować odkomentowując deklarację przyjaźni w klasie Me.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
/* File: you.cpp */
#include "friend.h"
namespace FriendNS
{
You::You(void)
{
this->yourData = 0;
}
You::You(int yourData)
{
this->yourData = yourData;
}
You::~You(void)
{
// sorry ;]
}
void You::setYourData(int yourData)
{
this->yourData = yourData;
return;
}
int You::getYourData(void)
{
return this->yourData;
}
/* void You::setMyData(Me *me, int myData)
{
me->myData = myData;
return;
}
int You::getMyData(Me *me)
{
return me->myData;
} */
}
|
Implementacja klasy You. Fragment objęty blokiem komentarza to implementacja zakomentowanych funkcji w deklaracji tej klasy.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
/* File: main.cpp */
#include "friend.h"
// go! go! go!
int main(void)
{
// construct objects
FriendNS::Me *me = new FriendNS::Me();
FriendNS::You *you = new FriendNS::You();
// call objects "native" methods
me->setMyData(100);
you->setYourData(50);
std::cout
<< "Native methods : "
<< "me='" << me->getMyData() << "' "
<< "you='" << you->getYourData() << "'"
<< std::endl;
// take advantage of class "friendship"
me->setYourData(you, 34);
// error C2039: 'setMyData' : is not a member of 'FriendNS::You'.
// you->setMyData(me, 89);
std::cout
<< "Class friendship: "
<< "me='" << me->getMyData() << "' "
<< "you='" << you->getYourData() << "'"
<< std::endl;
// success at the end
return EXIT_SUCCESS;
}
|
Plik zawierający funkcję main(), w której prezentowane jest praktyczne wykorzystanie powyższych klas.
Wyjaśnienie: klasa “Me” ma, ze względu na deklarację:
1
2
|
public:
friend class Me;
|
dostęp do prywatnej części klasy “You”, w związku z czym możliwe jest wykonanie metody:
1
2
3
4
5
|
void Me::setYourData(You *you, int yourData)
{
you->yourData = yourData;
return;
}
|
Z drugiej strony przyjaźń pomiędzy klasami bez wyraźnej deklaracji w każdej z nich nie jest możliwa, zatem “You” nie ma dostępu do atrybutów “Me” [zakomentowane fragmenty kodu]. Tak więc wyraźnie widać, że “co je twoje, to je moje, a co moje, to nie rusz!” ;] Oczywiście nie trzeba deklarować przyjaźni dla całych klas, można zrobić to tylko dla poszczególnych funkcji, ale to pozostawiam już Wam w ramach samodzielnej nauki, link do Wikipedii znajduje się na końcu artykułu.
Linki:
[1] http://www.cplusplus.com/doc/tutorial/inheritance.html - artykuł o klasach i funkcjach zaprzyjaźnionych + opis i porównanie z mechanizmem dziedziczenia.
[2] http://en.wikipedia.org/wiki/Friend_class - artykuł na angielskiej Wikipedii na temat klas zaprzyjaźnionych.
[3] http://en.wikipedia.org/wiki/Friend_function - artykuł na angielskiej Wikipedii dotyczący zaprzyjaźnionych funkcji.