Friend-Funktionen und -Klassen
Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy. Lektion 32 von 104.
Manchmal benötigt eine Funktion oder Klasse außerhalb deiner Klasse Zugriff auf ihre privaten Mitglieder. Anstatt diese Mitglieder öffentlich zu machen, stellt C++ das Schlüsselwort friend bereit, um selektiven Zugriff zu gewähren und gleichzeitig die Kapselung beizubehalten.
Eine Friend-Funktion wird innerhalb einer Klasse deklariert, ist aber kein Mitglied dieser Klasse. Sie kann auf die privaten und geschützten Mitglieder der Klasse zugreifen:
class Box {
double width;
public:
Box(double w) : width(w) {}
// Deklariere printWidth als friend
friend void printWidth(const Box& b);
};
// Friend-Funktionsdefinition – KEINE Member-Funktion
void printWidth(const Box& b) {
std::cout << b.width; // Kann auf privates Member zugreifen
}Beachte, dass printWidth außerhalb der Klasse ohne den Operator zur Bereichsauflösung definiert ist. Es handelt sich um eine reguläre Funktion, die zufällig über besondere Zugriffsrechte verfügt.
Du kannst auch eine ganze Klasse zu einem Freund machen und dadurch allen ihren Memberfunktionen Zugriff auf private Member gewähren:
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 friend
}
};Freundschaft ist nicht gegenseitig – wenn Engine Mechanic als Freund deklariert, kann Mechanic auf die privaten Member von Engine zugreifen, aber nicht umgekehrt. Verwende Freunde sparsam, da sie eine enge Kopplung zwischen Klassen erzeugen. Sie sind besonders nützlich für die Operatorüberladung und eng verwandte Hilfsfunktionen.
Aufgabe
EinfachWir erstellen ein sicheres Nachrichtensystem, 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 Freundfunktionen und Freundklassen.
Du erstellst zwei Dateien, um deinen Code zu organisieren:
SecureMessage.h: Definiere eineSecureMessage-Klasse, die private Nachrichtendaten speichert und vertrauenswürdigen Entitäten selektiven Zugriff gewährt. Deine Klasse sollte Folgendes enthalten:- Private Member:
content(string) undsecretKey(int) - Einen Konstruktor, der den Inhalt und den geheimen Schlüssel entgegennimmt
MessageReaderals Freundklasse deklarieren, damit sie auf private Member zugreifen kann- Eine Freundfunktion
decryptMessage(const SecureMessage& msg, int key)deklarieren, die den Inhalt nur zurückgibt, wenn der bereitgestellte Schlüssel mit dem geheimen Schlüssel übereinstimmt, andernfalls"Access Denied"zurückgibt
Definiere anschließend die
MessageReader-Klasse in derselben Header-Datei:- Eine Methode
readContent(const SecureMessage& msg), die direkt auf den privatencontentder Nachricht zugreift und ihn zurückgibt - Eine Methode
verifyKey(const SecureMessage& msg, int key), die prüft, ob der bereitgestellte Schlüssel mit demsecretKeyder Nachricht übereinstimmt, und"Valid"oder"Invalid"zurückgibt
Definiere schließlich die Freundfunktion
decryptMessagenach den Klassen.- Private Member:
main.cpp: Zeige sowohl Freundfunktionen als auch Freundklassen in Aktion. Lies einen Nachrichteninhalt, einen geheimen Schlüssel und einen Testschlüssel aus der Eingabe ein (jeweils in einer separaten Zeile), und:- Erstelle eine
SecureMessagemit dem Inhalt und dem geheimen Schlüssel - Erstelle ein
MessageReader-Objekt - Gib
"Reader access: <content>"mithilfe der MethodereadContent()des Readers aus - Gib
"Key verification: <result>"mithilfe vonverifyKey()mit dem Testschlüssel aus - Gib
"Decrypt with test key: <result>"mithilfe der Freundfunktion mit dem Testschlüssel aus - Gib
"Decrypt with correct key: <result>"mithilfe der Freundfunktion mit dem ursprünglichen geheimen Schlüssel aus
- Erstelle eine
Denke daran, dass Freundfunktionen außerhalb der Klasse ohne den Bereichsauflösungsoperator definiert werden: Es handelt sich um normale Funktionen mit besonderen Zugriffsrechten. Die Klasse MessageReader kann als Freundklasse in all ihren Methoden direkt auf die privaten Member von SecureMessage zugreifen.
Konvertiere die Schlüsseleingaben mithilfe von std::stoi() von Zeichenketten in Ganzzahlen. Binde deine Header-Datei in main.cpp mithilfe von #include "SecureMessage.h" ein.
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
// TODO: Erstelle ein MessageReader-Objekt
// TODO: Print "Reader access: <content>" using readContent()
// TODO: Print "Key verification: <result>" using verifyKey() with test key
// TODO: Print "Decrypt with test key: <result>" using decryptMessage with test key
// TODO: Print "Decrypt with correct key: <result>" using decryptMessage with secret key
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 & QuelldateienNamespaces & GültigkeitsbereichEinführung in OOP mit C++Klassen vs. ObjekteDer „this“-ZeigerMethoden (Member-Funktionen)Attribute (Datenmember)Grundlagen zu Ctors & DtorsRückblick – Einfacher Taschenrechner4Klassenattribute
Instanz- vs. statische MemberGetter und SetterConst-MemberfunktionenMutable-SchlüsselwortStatische Methoden und VariablenFriend-Funktionen und -KlassenZusammenfassung – Bankkontoverwaltung7Vererbung
Grundlagen der VererbungZugriffsebenen bei der VererbungAufrufreihenfolge von Ctor & DtorÜberschreiben von MethodenVirtuelle Funktionen & VTableMehrfachvererbungVirtuelle VererbungRückblick – Mitarbeiterhierarchie2Speicherverwaltung
Stack- vs. Heap-SpeicherZeiger und ReferenzenDynamischer Speicher (new/delete)Intelligente Zeiger in C++RAII in C++Rückblick – Dynamischer Array-Manager5Kapselung
Zugriffsbezeichner in C++Zugriffsbezeichner im DetailInformationsverbergungStruct vs. KlasseVerschachtelte und innere KlassenWiederholung – Studierendenverwaltungssystem8Polymorphie
Polymorphie zur Compilezeit vs. LaufzeitFunktionsüberladungVirtuelle Funktionen erneut betrachtetRein virtuelle FunktionenAbstrakte KlassenInterface-Design in C++Dynamic Casting & RTTIRückblick – Shape-Calculator3Konstruktoren & Destruktoren
StandardkonstruktorParametrisierter KonstruktorKopierkonstruktorMove-KonstruktorKonstruktor-InitialisierungslistenDelegierende KonstruktorenDestruktor im DetailDrei-/Fünf-/Null-RegelZusammenfassung – String-Klasse6Operatorüberladung
Einführung in die OperatorüberladungArithmetische OperatorüberladungVergleichsoperatorüberladungStream-OperatorenZuweisungsoperatorüberladungÜberladung der Operatoren [] und ()TypkonvertierungsoperatorenWiederholung – Matrix-Klasse9Templates
FunktionstemplatesKlassen-TemplatesTemplate-SpezialisierungVariadische TemplatesGrundlagen zu SFINAE und Type TraitsRückblick – Generischer ContainerÜbe selbstständig: Online-C++-Compiler