Menu
Coddy logo textTech

Funkcje i klasy zaprzyjaźnione

Część sekcji Programowanie obiektowe ścieżki C++ w Coddy. Lekcja 32 z 104.

Czasami funkcja lub klasa spoza Twojej klasy potrzebuje dostępu do jej prywatnych składowych. Zamiast upubliczniać te składowe, C++ udostępnia słowo kluczowe friend, które pozwala przyznać wybranym elementom dostęp, zachowując jednocześnie hermetyzację.

Funkcja zaprzyjaźniona jest deklarowana wewnątrz klasy, ale nie jest jej składową. Może uzyskiwać dostęp do prywatnych i chronionych składowych klasy:

class Box {
    double width;
    
public:
    Box(double w) : width(w) {}
    
    // Zadeklaruj printWidth jako funkcję zaprzyjaźnioną
    friend void printWidth(const Box& b);
};

// Definicja funkcji zaprzyjaźnionej – NIE jest metodą klasy
void printWidth(const Box& b) {
    std::cout << b.width;    // Może uzyskać dostęp do prywatnego pola
}

Zauważ, że printWidth jest zdefiniowana poza klasą, bez operatora rozstrzygania zakresu. To zwykła funkcja, która ma po prostu specjalne uprawnienia dostępu.

Możesz też uznać całą klasę za zaprzyjaźnioną, przyznając wszystkim jej funkcjom składowym dostęp do składowych prywatnych:

class Engine {
    int horsepower;
    
    friend class Mechanic;    // Mechanic ma dostęp do prywatnych składowych
    
public:
    Engine(int hp) : horsepower(hp) {}
};

class Mechanic {
public:
    void diagnose(const Engine& e) {
        std::cout << e.horsepower;    // Dozwolone — Mechanic jest klasą zaprzyjaźnioną
    }
};

Przyjaźń nie jest wzajemna — jeśli Engine deklaruje Mechanic jako przyjaciela, Mechanic może uzyskać dostęp do prywatnych składowych Engine, ale nie odwrotnie. Używaj deklaracji przyjaźni oszczędnie, ponieważ powodują silne sprzężenie między klasami. Są najbardziej przydatne przy przeciążaniu operatorów i w przypadku ściśle powiązanych funkcji pomocniczych.

challenge icon

Wyzwanie

Łatwy

Zbudujmy bezpieczny system przesyłania wiadomości, w którym klasa MessageReader potrzebuje specjalnego dostępu, aby odczytywać zaszyfrowane wiadomości przechowywane w klasie SecureMessage. To idealny przypadek do użycia funkcji zaprzyjaźnionych i klas zaprzyjaźnionych.

Utworzysz dwa pliki, aby uporządkować kod:

  • SecureMessage.h: Zdefiniuj klasę SecureMessage, która przechowuje prywatne dane wiadomości i przyznaje wybranym zaufanym elementom dostęp do nich. Twoja klasa powinna mieć:
    • Pola prywatne: content (string) i secretKey (int)
    • Konstruktor przyjmujący treść i klucz tajny
    • Zadeklaruj MessageReader jako klasę zaprzyjaźnioną, aby mogła uzyskiwać dostęp do pól prywatnych
    • Zadeklaruj zaprzyjaźnioną funkcję decryptMessage(const SecureMessage& msg, int key), która zwraca treść tylko wtedy, gdy podany klucz pasuje do klucza tajnego; w przeciwnym razie zwraca "Access Denied"

    Następnie zdefiniuj klasę MessageReader w tym samym pliku nagłówkowym:

    • Metodę readContent(const SecureMessage& msg), która bezpośrednio uzyskuje dostęp do prywatnego pola content wiadomości i je zwraca
    • Metodę verifyKey(const SecureMessage& msg, int key), która sprawdza, czy podany klucz pasuje do pola secretKey wiadomości, i zwraca "Valid" lub "Invalid"

    Na koniec zdefiniuj zaprzyjaźnioną funkcję decryptMessage po definicjach klas.

  • main.cpp: Pokaż działanie zarówno funkcji zaprzyjaźnionych, jak i klas zaprzyjaźnionych. Wczytaj treść wiadomości, klucz tajny i klucz testowy ze standardowego wejścia (każdy w osobnym wierszu), a następnie:
    • Utwórz obiekt SecureMessage z treścią i kluczem tajnym
    • Utwórz obiekt klasy MessageReader
    • Wypisz "Reader access: <content>", używając metody readContent() czytnika
    • Wypisz "Key verification: <result>", używając metody verifyKey() z kluczem testowym
    • Wypisz "Decrypt with test key: <result>", używając zaprzyjaźnionej funkcji z kluczem testowym
    • Wypisz "Decrypt with correct key: <result>", używając zaprzyjaźnionej funkcji z oryginalnym kluczem tajnym

Pamiętaj, że funkcje zaprzyjaźnione definiuje się poza klasą, bez operatora rozstrzygania zakresu: są to zwykłe funkcje ze specjalnymi uprawnieniami dostępu. Klasa MessageReader, jako klasa zaprzyjaźniona, może bezpośrednio uzyskiwać dostęp do prywatnych pól klasy SecureMessage we wszystkich swoich metodach.

Przekonwertuj klucze wczytane jako ciągi znaków na liczby całkowite za pomocą std::stoi(). Dołącz plik nagłówkowy w pliku main.cpp, używając #include "SecureMessage.h".

Spróbuj swoich sił

#include <iostream>
#include <string>
#include "SecureMessage.h"
using namespace std;

int main() {
    // Wczytaj dane wejściowe
    string messageContent;
    string secretKeyStr;
    string testKeyStr;
    
    getline(cin, messageContent);
    getline(cin, secretKeyStr);
    getline(cin, testKeyStr);
    
    int secretKey = stoi(secretKeyStr);
    int testKey = stoi(testKeyStr);
    
    // TODO: Utwórz SecureMessage z treścią i tajnym kluczem
    
    // TODO: Utwórz obiekt MessageReader
    
    // TODO: Wypisz "Reader access: <content>", używając readContent()
    
    // TODO: Wypisz "Key verification: <result>", używając verifyKey() z kluczem testowym
    
    // TODO: Wypisz "Decrypt with test key: <result>", używając decryptMessage z kluczem testowym
    
    // TODO: Wypisz "Decrypt with correct key: <result>", używając decryptMessage z tajnym kluczem
    
    return 0;
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator C++ online