C++-manual | ||
---|---|---|
Förutgående | Kapitel 17. Mera om klasser | Nästa |
Det finns vissa fall då man skulle vilja att klasser skulle kunna bryta mot de skyddsregler som gäller för privata data i klasser. Med detta avses att en viss klass eller funktion skulle kunna accessera de medlemmar och metoder som är deklarerade som private i en klass. En situation då ett dylikt förfarande kunde vara praktiskt är då vi t.ex. har en klass Image som innehåller en bild, och en klass ImageViewer som visar bilder på skärmen. I normala fall kan inga externa exntiter ändra på bildens innehåll, d.v.s. pixeldata, men vi skulle vilja att bildvisaren har vissa möjligheter att korrigera t.ex. storleken eller små fel som uppstår vid förstoring eller rotation av bilden. Det är inte möjligt utan specialarrangemang. Vi kunde tänka oss följande förenklade definitioner på klasserna.
class Image { public: // konstruktor Image (unsigned int Width, unsigned int Height, int * Pixels); // visa bilden void show (); private: // bildens storlek unsigned int m_Width; unsigned int m_Height; // pixeldata (i något internt format) int * m_Pixels; }; class ImageViewer { public: // konstruktor ImageViewer (); // ladda en bild från disk Image * load (string Filename); // visa en bild void show (const Image & Picture); // korrigera en bild enligt någon algorithm void fix (Image & Picture); private: // lista av alla bilder list m_Images; }; Image * ImageViewer::load (string Filename) { // läs data från filen in i lokala variabler unsigned int Width = ...; unsigned int Height = ...; int * Data = ...; // skapa ny bild Image * Picture = new Image ( Width, Height, Data ); // fixa små fel i bilden fix ( *Picture ); // returera den nya bilden vi laddat in return New; } void ImageViewer::fix (Image & Picture) { // accessera pixeldata för 'Picture' enligt fin algoritm ... } |
Vi går inte närmare in på själva koden för dessa klasser, utan koncentrerar oss på gränssnittet de presenterar utåt. Klassen Image har en privat medlem m_Pixels som innehåller alla pixlar för en bild. Metoden fix() i ImageViewer vill kunna direkt accessera pixeldata i bilden Picture. Det är inte möjligt, utan kompilatorn kommer att ge oss ett felmeddelande om att vi inte har rätt att accessera privata data i Image. Lösningen är att Image deklarerar klassen ImageViewer som en pålitlig "vän" (friend). En vänklass har rätt att accessera private data i en annan.
Bryter inte detta mot grundprinciperna i objektorientering då? Klassers privata data skall vara privat, anser purister. Inte egentligen eftersom det i detta fall är Image som måste explicit säga att ImageViewer är dess friend. Det går inte andra vägen. Den klass som blir accesserad måste explicit tillåta andra klasser att göra detta. Så det är fråhan om ett medvetet designbeslut man gjort då man skrivit en klass. För att deklarera en klass för en friend används förljande allmänna form:
friend class klassnamn; |
som placeras någonstans inne i den klass vars privata data skall kunna accesseras av klassnamn. I vårt fall kunde vi ändra definitionen av klassen Image till:
class Image { // deklarera en vän friend class ImageViewer; public: // konstruktor Image (unsigned int Width, unsigned int Height, int * Pixels); // visa bilden void show (); private: // bildens storlek unsigned int m_Width; unsigned int m_Height; // pixeldata (i något internt format) int * m_Pixels; }; |
Enda skillnaden är friend-deklarationen. Nu kan ImageViewer accessera alla privata data i Image som om de vore public.
Om vi tittar lite närmare på vår klass ImageViewer märker vi snabbt att det är endast metoden fix() som behöver direkt access till Image. Vi kunde alltså med fördel begränsa så att endast denna metod kan accessera privata data, istället för att alla metoder kan göra det:
class Image { // deklarera en vän friend class ImageViewer::fix (Image & Picture); public: ... }; |
Vi skriver nu in hela metodens namn med klassnamn, metodnamn och alla parametrar, exakt som den deklareras i ImageViewer. På detta sätt kan t.ex. load() inte länge accessera privata data. Det kan vara en god ide att begränsa denna access till exakt de metoder som behöver den, för att inte i misstag i andra metoder accessa privata data när det egentligen inte behövs. Att använda friend är inte en lösning på problemet med en dåligt konstruerad klasshierarki. I så fall är det bättre att designa om sina klasser.
En klass kan ha multipla vänner om så skulle behövas. Man placerar bara en ny definition någonstans i klassdefinitionen. Det är ingen skillnad var man placerar friend-deklarationer, d.v.s. inom public, protected eller private, så länge som de är separata deklarationer.
Förutgående | Hem | Nästa |
Mera om klasser | Upp | Polymorfism |