Friend-Funktionen & Klassen
Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy — Lektion 32 von 104.
Manchmal benötigt eine Funktion oder Klasse außerhalb Ihrer Klasse Zugriff auf deren private Member. Anstatt diese Member öffentlich zu machen, bietet C++ das Schlüsselwort friend an, um selektiven Zugriff zu gewähren und gleichzeitig die Kapselung beizubehalten.
Eine Friend-Funktion wird innerhalb einer Klasse deklariert, ist aber kein Element dieser Klasse. Sie kann auf die privaten und geschützten Elemente der Klasse zugreifen:
class Box {
double width;
public:
Box(double w) : width(w) {}
// Deklariere printWidth als Friend
friend void printWidth(const Box& b);
};
// Definition der Friend-Funktion - KEINE Member-Funktion
void printWidth(const Box& b) {
std::cout << b.width; // Kann auf private Member zugreifen
}Beachten Sie, dass printWidth außerhalb der Klasse ohne den Bereichsauflösungsoperator definiert ist. Es handelt sich um eine reguläre Funktion, die zufällig über spezielle Zugriffsberechtigungen verfügt.
Sie können auch eine gesamte Klasse als Friend deklarieren, wodurch alle ihre Elementfunktionen Zugriff auf private Member erhalten:
class Engine {
int horsepower;
friend class Mechanic; // Mechanic kann auf private Member zugreifen
public:
Engine(int hp) : horsepower(hp) {}
};
class Mechanic {
public:
void diagnose(const Engine& e) {
std::cout << e.horsepower; // Erlaubt - Mechanic ist ein Freund
}
};Freundschaft ist nicht wechselseitig – wenn Engine die Klasse Mechanic als Freund deklariert, kann Mechanic auf die privaten Member von Engine zugreifen, aber nicht umgekehrt. Verwenden Sie Freunde sparsam, da sie eine enge Kopplung zwischen Klassen erzeugen. Sie sind am nützlichsten für die Operatorüberladung und eng verwandte Hilfsfunktionen.
Aufgabe
EinfachLassen Sie uns ein sicheres Nachrichtensystem bauen, bei dem eine MessageReader-Klasse speziellen Zugriff benötigt, um verschlüsselte Nachrichten zu lesen, die in einer SecureMessage-Klasse gespeichert sind. Dies ist ein perfektes Szenario für die Verwendung von Friend-Funktionen und Friend-Klassen.
Sie werden zwei Dateien erstellen, um Ihren Code zu organisieren:
SecureMessage.h: Definieren Sie eineSecureMessage-Klasse, die private Nachrichtendaten speichert und vertrauenswürdigen Entitäten selektiven Zugriff gewährt. Ihre Klasse sollte Folgendes haben:- Private Member:
content(string) undsecretKey(int) - Einen Konstruktor, der den Inhalt und den geheimen Schlüssel entgegennimmt
- Deklarieren Sie
MessageReaderals Friend-Klasse, damit sie auf private Member zugreifen kann - Deklarieren Sie eine Friend-Funktion
decryptMessage(const SecureMessage& msg, int key), die den Inhalt nur zurückgibt, wenn der angegebene Schlüssel mit dem geheimen Schlüssel übereinstimmt, andernfalls"Access Denied"zurückgibt
Definieren Sie dann die
MessageReader-Klasse in derselben Header-Datei:- Eine Methode
readContent(const SecureMessage& msg), die direkt auf den privatencontentder Nachricht zugreift und diesen zurückgibt - Eine Methode
verifyKey(const SecureMessage& msg, int key), die prüft, ob der angegebene Schlüssel mit demsecretKeyder Nachricht übereinstimmt, und"Valid"oder"Invalid"zurückgibt
Definieren Sie schließlich die Friend-Funktion
decryptMessagenach den Klassen.- Private Member:
main.cpp: Demonstrieren Sie sowohl Friend-Funktionen als auch Friend-Klassen in Aktion. Lesen Sie einen Nachrichteninhalt, einen geheimen Schlüssel und einen Testschlüssel von der Eingabe (jeweils in einer separaten Zeile) und führen Sie dann Folgendes aus:- Erstellen Sie eine
SecureMessagemit dem Inhalt und dem geheimen Schlüssel - Erstellen Sie ein
MessageReader-Objekt - Geben Sie
"Reader access: <content>"unter Verwendung derreadContent()-Methode des Readers aus - Geben Sie
"Key verification: <result>"unter Verwendung vonverifyKey()mit dem Testschlüssel aus - Geben Sie
"Decrypt with test key: <result>"unter Verwendung der Friend-Funktion mit dem Testschlüssel aus - Geben Sie
"Decrypt with correct key: <result>"unter Verwendung der Friend-Funktion mit dem ursprünglichen geheimen Schlüssel aus
- Erstellen Sie eine
Denken Sie daran, dass Friend-Funktionen außerhalb der Klasse ohne den Bereichsauflösungsoperator definiert werden — es sind reguläre Funktionen mit speziellen Zugriffsberechtigungen. Die MessageReader-Klasse kann als Friend in all ihren Methoden direkt auf die privaten Member von SecureMessage zugreifen.
Konvertieren Sie die Schlüsseleingaben mit std::stoi() von Strings in Integer. Binden Sie Ihre Header-Datei in main.cpp mit #include "SecureMessage.h" ein.
Spickzettel
Das Schlüsselwort friend gewährt selektiven Zugriff auf private Member, während die Kapselung beibehalten wird.
Friend-Funktion: Eine innerhalb einer Klasse deklarierte Funktion, die auf deren private und geschützte Member zugreifen kann, aber kein Member der Klasse ist:
class Box {
double width;
public:
Box(double w) : width(w) {}
// Friend-Funktion deklarieren
friend void printWidth(const Box& b);
};
// Außerhalb der Klasse ohne Bereichsauflösungsoperator definieren
void printWidth(const Box& b) {
std::cout << b.width; // Kann auf privaten Member zugreifen
}Friend-Klasse: Eine gesamte Klasse kann als Friend deklariert werden, wodurch alle ihre Member-Funktionen Zugriff auf private Member erhalten:
class Engine {
int horsepower;
friend class Mechanic; // Alle Mechanic-Methoden können auf private Member zugreifen
public:
Engine(int hp) : horsepower(hp) {}
};
class Mechanic {
public:
void diagnose(const Engine& e) {
std::cout << e.horsepower; // Erlaubt
}
};Wichtige Punkte:
- Freundschaft ist nicht wechselseitig – wenn A B als Friend deklariert, kann B auf die privaten Member von A zugreifen, aber nicht umgekehrt
- Friend-Funktionen werden außerhalb der Klasse ohne den Bereichsauflösungsoperator definiert
- Verwenden Sie Friends sparsam, da sie eine enge Kopplung zwischen Klassen erzeugen
- Am nützlichsten für Operatorüberladung und eng verwandte Hilfsfunktionen
Probier es selbst
#include <iostream>
#include <string>
#include "SecureMessage.h"
using namespace std;
int main() {
// Eingabe lesen
string messageContent;
string secretKeyStr;
string testKeyStr;
getline(cin, messageContent);
getline(cin, secretKeyStr);
getline(cin, testKeyStr);
int secretKey = stoi(secretKeyStr);
int testKey = stoi(testKeyStr);
// TODO: Erstelle eine SecureMessage mit dem Inhalt und dem geheimen Schlüssel (secret key)
// TODO: Erstelle ein MessageReader-Objekt
// TODO: Gib "Reader access: <content>" unter Verwendung von readContent() aus
// TODO: Gib "Key verification: <result>" unter Verwendung von verifyKey() mit dem Test-Schlüssel (test key) aus
// TODO: Gib "Decrypt with test key: <result>" unter Verwendung von decryptMessage mit dem Test-Schlüssel (test key) aus
// TODO: Gib "Decrypt with correct key: <result>" unter Verwendung von decryptMessage mit dem geheimen Schlüssel (secret key) aus
return 0;
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienC++ Build & KompilierungHeader-Dateien & QuelldateienNamensräume & GültigkeitsbereicheEinführung in OOP in C++Klassen vs. ObjekteDer 'this'-PointerMethoden (Elementfunktionen)Attribute (Datenelemente)Grundlagen zu Ctors & DtorsRückblick - Einfacher Taschenrechner4Klasseneigenschaften
Instanz- vs. statische MemberGetter und SetterConst-MemberfunktionenMutable-SchlüsselwortStatische Methoden und VariablenFriend-Funktionen & KlassenZusammenfassung - Bankkonto-Manager7Vererbung
Grundlagen der VererbungZugriffsstufen bei VererbungAufrufreihenfolge von Ctor & DtorÜberschreiben von MethodenVirtuelle Funktionen & VTableMehrfachvererbungVirtuelle VererbungRückblick - Mitarbeiter-Hierarchie2Speicherverwaltung
Stack vs. Heap SpeicherZeiger und ReferenzenDynamischer Speicher (new/delete)Smart Pointers in C++RAII in C++Rückblick – Dynamischer Array-Manager5Kapselung
Zugriffsspezifizierer in C++Zugriffsspezifizierer im DetailInformation HidingStruct vs. ClassVerschachtelte & innere KlassenRückblick - Studentenverwaltungssystem8Polymorphie
Kompilierzeit- vs. Laufzeit-PolymorphieFunktionsüberladungVirtuelle Funktionen – WiederholungRein virtuelle FunktionenAbstrakte KlassenInterface-Design in C++Dynamic Casting & RTTIZusammenfassung – Formen-Rechner3Konstruktoren & Destruktoren
StandardkonstruktorParametrisierter KonstruktorKopierkonstruktorVerschiebekonstruktorKonstruktor-InitialisierungslistenDelegierende KonstruktorenDestruktoren im DetailRule of Three / Five / ZeroZusammenfassung - String-Klasse6Operatorüberladung
Einführung in die OperatorüberladungArithmetische OperatorüberladungVergleichsoperatorüberladungStream-OperatorenZuweisungsoperatorüberladungÜberladen der [] und () OperatorenTypumwandlungsoperatorenRückblick - Matrix-Klasse9Templates
FunktionstemplatesKlassentemplatesTemplate-SpezialisierungVariadische TemplatesSFINAE & Type Traits GrundlagenRückblick - Generische Container