Funzioni e classi friend
Fa parte della sezione Programmazione orientata agli oggetti del percorso C++ di Coddy. Lezione 32 di 104.
A volte una funzione o una classe esterna alla tua classe ha bisogno di accedere ai suoi membri privati. Invece di rendere pubblici quei membri, C++ fornisce la parola chiave friend per concedere un accesso selettivo mantenendo l'incapsulamento.
Una funzione friend è dichiarata all'interno di una classe, ma non è un membro di quella classe. Può accedere ai membri privati e protetti della classe:
class Box {
double width;
public:
Box(double w) : width(w) {}
// Dichiara printWidth come funzione friend
friend void printWidth(const Box& b);
};
// Definizione della funzione friend - NON è una funzione membro
void printWidth(const Box& b) {
std::cout << b.width; // Può accedere al membro privato
}Nota che printWidth è definita al di fuori della classe senza l'operatore di risoluzione dell'ambito. È una funzione normale che, per caso, dispone di privilegi di accesso speciali.
Puoi anche rendere amica un'intera classe, concedendo a tutte le sue funzioni membro l'accesso ai membri privati:
class Engine {
int horsepower;
friend class Mechanic; // Mechanic può accedere ai membri privati
public:
Engine(int hp) : horsepower(hp) {}
};
class Mechanic {
public:
void diagnose(const Engine& e) {
std::cout << e.horsepower; // Consentito - Mechanic è una classe friend
}
};L'amicizia non è reciproca: se Engine dichiara Mechanic come amico, Mechanic può accedere ai membri privati di Engine, ma non viceversa. Usa gli amici con parsimonia, perché creano un forte accoppiamento tra le classi. Sono più utili per l'overloading degli operatori e per le funzioni helper strettamente correlate.
Sfida
FacileCostruiamo un sistema di messaggistica sicuro in cui una classe MessageReader deve avere accesso speciale per leggere i messaggi crittografati memorizzati in una classe SecureMessage. Questo è uno scenario perfetto per usare funzioni amiche e classi amiche.
Creerai due file per organizzare il codice:
SecureMessage.h: definisci una classeSecureMessageche memorizza i dati privati del messaggio e concede accesso selettivo alle entità fidate. La tua classe dovrebbe avere:- Membri privati:
content(stringa) esecretKey(int) - Un costruttore che accetta il contenuto e la chiave segreta
- Dichiara
MessageReadercome classe amica affinché possa accedere ai membri privati - Dichiara una funzione amica
decryptMessage(const SecureMessage& msg, int key)che restituisce il contenuto solo se la chiave fornita corrisponde alla chiave segreta; altrimenti restituisce"Access Denied"
Definisci poi la classe
MessageReadernello stesso file header:- Un metodo
readContent(const SecureMessage& msg)che accede direttamente al membro privatocontentdel messaggio e lo restituisce - Un metodo
verifyKey(const SecureMessage& msg, int key)che verifica se la chiave fornita corrisponde asecretKeydel messaggio e restituisce"Valid"oppure"Invalid"
Infine, definisci la funzione amica
decryptMessagedopo le classi.- Membri privati:
main.cpp: mostra in azione sia le funzioni amiche sia le classi amiche. Leggi da input il contenuto di un messaggio, una chiave segreta e una chiave di prova (ciascuno su una riga separata), quindi:- Crea un oggetto
SecureMessagecon il contenuto e la chiave segreta - Crea un oggetto
MessageReader - Stampa
"Reader access: <content>"usando il metodoreadContent()del lettore - Stampa
"Key verification: <result>"usandoverifyKey()con la chiave di prova - Stampa
"Decrypt with test key: <result>"usando la funzione amica con la chiave di prova - Stampa
"Decrypt with correct key: <result>"usando la funzione amica con la chiave segreta originale
- Crea un oggetto
Ricorda che le funzioni amiche vengono definite fuori dalla classe senza l'operatore di risoluzione dell'ambito: sono funzioni normali con privilegi di accesso speciali. La classe MessageReader, in quanto amica, può accedere direttamente ai membri privati di SecureMessage in tutti i suoi metodi.
Converti gli input delle chiavi da stringhe a interi usando std::stoi(). Includi il tuo file header in main.cpp usando #include "SecureMessage.h".
Provalo tu
#include <iostream>
#include <string>
#include "SecureMessage.h"
using namespace std;
int main() {
// Lee la entrada
string messageContent;
string secretKeyStr;
string testKeyStr;
getline(cin, messageContent);
getline(cin, secretKeyStr);
getline(cin, testKeyStr);
int secretKey = stoi(secretKeyStr);
int testKey = stoi(testKeyStr);
// TODO: Crea un SecureMessage con el contenido y la clave secreta
// TODO: Crea un objeto MessageReader
// TODO: Imprime "Reader access: <content>" usando readContent()
// TODO: Imprime "Key verification: <result>" usando verifyKey() con la clave de prueba
// TODO: Imprime "Decrypt with test key: <result>" usando decryptMessage con la clave de prueba
// TODO: Imprime "Decrypt with correct key: <result>" usando decryptMessage con la clave secreta
return 0;
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti della programmazione orientata agli oggetti
File esterniBuild e compilazione in C++File header e file sorgenteNamespace e ambitoIntroduzione alla programmazione orientata agli oggetti in C++Classi e oggetti a confrontoIl puntatore 'this'Metodi (funzioni membro)Attributi (membri dati)Fondamenti di costruttori e distruttoriRiepilogo - Calcolatrice semplice4Proprietà delle classi
Membri di istanza e staticiGetter e setterFunzioni membro constParola chiave mutableMetodi e variabili staticiFunzioni e classi friendRiepilogo - Gestore di conti bancari7Ereditarietà
Ereditarietà di baseLivelli di accesso nell’ereditarietàOrdine di chiamata di costruttori e distruttoriRidefinizione dei metodiFunzioni virtuali e VTableEreditarietà multiplaEreditarietà virtualeRiepilogo - Gerarchia dei dipendenti2Gestione della memoria
Memoria Stack vs HeapPuntatori e riferimentiMemoria dinamica (new/delete)Puntatori intelligenti in C++RAII in C++Riepilogo - Gestore di array dinamico5Incapsulamento
Specificatori di accesso in C++Specificatori di accesso in dettaglioOccultamento delle informazioniStruct vs classClassi annidate e interneRiepilogo - Sistema di registrazione degli studenti8Polimorfismo
Polimorfismo a compile time e a runtimeOverload delle funzioniFunzioni virtuali: ripassoFunzioni virtuali pureClassi astratteProgettazione delle interfacce in C++Dynamic casting e RTTIRipasso: calcolatrice di forme11Concetti avanzati di OOP
Composizione vs ereditarietàMixin tramite CRTPIdiom PimplType ErasureEnum class e tipizzazione forteGestione delle eccezioni in OOPGerarchie personalizzate di eccezioni14Pattern di progettazione - Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositeRAII come pattern3Costruttori e distruttori
Costruttore predefinitoCostruttore con parametriCostruttore di copiaCostruttore di spostamentoListe di inizializzazione dei costruttoriCostruttori delegantiApprofondimento sui distruttoriRegola del Tre / Cinque / ZeroRipasso - classe String6Sovraccarico degli operatori
Introduzione al sovraccarico degli operatoriSovraccarico degli operatori aritmeticiSovraccarico degli operatori di confrontoOperatori di flussoSovraccarico dell'operatore di assegnazioneSovraccarico degli operatori [] e ()Operatori di conversione di tipoRipasso - Classe Matrix9Template
Template di funzioneTemplate di classeSpecializzazione dei templateTemplate variadiciBasi di SFINAE e dei trait di tipoRiepilogo - Contenitore genericoEsercitati da solo: Compilatore C++ online