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:

/* 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++.

/* 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.

/* 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 ;]

/* 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.

/* 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.

/* 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ę:

	public:
	friend class Me;

dostęp do prywatnej części klasy “You”, w związku z czym możliwe jest wykonanie metody:

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.