Fonctions et classes amies
Fait partie de la section Programmation Orientée Objet du Journey C++ de Coddy. Leçon 32 sur 104.
Parfois, une fonction ou une classe extérieure à votre classe doit accéder à ses membres privés. Plutôt que de rendre ces membres publics, C++ fournit le mot-clé friend pour accorder un accès sélectif tout en préservant l’encapsulation.
Une fonction amie est déclarée à l’intérieur d’une classe, mais n’est pas membre de cette classe. Elle peut accéder aux membres privés et protégés de la classe :
class Box {
double width;
public:
Box(double w) : width(w) {}
// Déclare printWidth comme friend
friend void printWidth(const Box& b);
};
// Définition de la fonction friend - PAS une fonction membre
void printWidth(const Box& b) {
std::cout << b.width; // Peut accéder au membre privé
}Notez que printWidth est défini en dehors de la classe, sans l’opérateur de résolution de portée. Il s’agit d’une fonction ordinaire qui bénéficie par hasard de privilèges d’accès particuliers.
Vous pouvez également déclarer toute une classe comme amie, en accordant à toutes ses fonctions membres l’accès aux membres privés :
class Engine {
int horsepower;
friend class Mechanic; // Mechanic peut accéder aux membres privés
public:
Engine(int hp) : horsepower(hp) {}
};
class Mechanic {
public:
void diagnose(const Engine& e) {
std::cout << e.horsepower; // Autorisé - Mechanic est un friend
}
};L’amitié n’est pas réciproque : si Engine déclare Mechanic comme ami, Mechanic peut accéder aux membres privés de Engine, mais pas l’inverse. Utilisez les amis avec parcimonie, car ils créent un couplage étroit entre les classes. Ils sont particulièrement utiles pour la surcharge d’opérateurs et les fonctions auxiliaires étroitement liées.
Défi
FacileConstruisons un système de messagerie sécurisé dans lequel une classe MessageReader a besoin d'un accès spécial pour lire les messages chiffrés stockés dans une classe SecureMessage. C'est un scénario idéal pour utiliser des fonctions amies et des classes amies.
Vous allez créer deux fichiers pour organiser votre code :
SecureMessage.h: définissez une classeSecureMessagequi stocke des données de message privées et accorde un accès sélectif aux entités de confiance. Votre classe doit comporter :- Membres privés :
content(chaîne) etsecretKey(entier) - Un constructeur qui prend le contenu et la clé secrète
- Déclarer
MessageReadercomme classe amie afin qu'elle puisse accéder aux membres privés - Déclarer une fonction amie
decryptMessage(const SecureMessage& msg, int key)qui renvoie le contenu uniquement si la clé fournie correspond à la clé secrète, et renvoie sinon"Access Denied"
Définissez ensuite la classe
MessageReaderdans le même fichier d'en-tête :- Une méthode
readContent(const SecureMessage& msg)qui accède directement aucontentprivé du message et le renvoie - Une méthode
verifyKey(const SecureMessage& msg, int key)qui vérifie si la clé fournie correspond ausecretKeydu message et renvoie"Valid"ou"Invalid"
Enfin, définissez la fonction amie
decryptMessageaprès les classes.- Membres privés :
main.cpp: démontrez le fonctionnement des fonctions amies et des classes amies. Lisez le contenu d'un message, une clé secrète et une clé de test depuis l'entrée (chacun sur une ligne distincte), puis :- Créez un objet
SecureMessageavec le contenu et la clé secrète - Créez un objet
MessageReader - Affichez
"Reader access: <content>"en utilisant la méthodereadContent()du lecteur - Affichez
"Key verification: <result>"en utilisantverifyKey()avec la clé de test - Affichez
"Decrypt with test key: <result>"en utilisant la fonction amie avec la clé de test - Affichez
"Decrypt with correct key: <result>"en utilisant la fonction amie avec la clé secrète d'origine
- Créez un objet
N'oubliez pas que les fonctions amies sont définies en dehors de la classe, sans l'opérateur de résolution de portée : ce sont des fonctions ordinaires bénéficiant de privilèges d'accès spéciaux. La classe MessageReader, étant une amie, peut accéder directement aux membres privés de SecureMessage dans toutes ses méthodes.
Convertissez les entrées de clé de chaînes en entiers à l'aide de std::stoi(). Incluez votre fichier d'en-tête dans main.cpp à l'aide de #include "SecureMessage.h".
Essayez vous-même
#include <iostream>
#include <string>
#include "SecureMessage.h"
using namespace std;
int main() {
// Lire l'entrée
string messageContent;
string secretKeyStr;
string testKeyStr;
getline(cin, messageContent);
getline(cin, secretKeyStr);
getline(cin, testKeyStr);
int secretKey = stoi(secretKeyStr);
int testKey = stoi(testKeyStr);
// TODO: Créer un SecureMessage avec le contenu et la clé secrète
// TODO: Créer un objet MessageReader
// 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;
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation Orientée Objet
1Fondamentaux de la POO
Fichiers externesGénération et compilation en C++Fichiers d’en-tête et fichiers sourceEspaces de noms et portéeIntroduction à la POO en C++Classes et objetsLe pointeur « this »Méthodes (fonctions membres)Attributs (membres de données)Bases des constructeurs et destructeursRécapitulatif - Calculatrice simple4Propriétés de classe
Membres d’instance et statiquesAccesseurs et mutateursFonctions membres constMot-clé mutableMéthodes et variables statiquesFonctions et classes amiesRécapitulatif - Gestionnaire de compte bancaire7Héritage
Héritage de baseNiveaux d’accès de l’héritageOrdre d’appel des constructeurs et destructeursRedéfinition des méthodesFonctions virtuelles et VTableHéritage multipleHéritage virtuelRécapitulatif : hiérarchie des employés10Vue d’ensemble de la STL
Vue d’ensemble et philosophie de la STLConteneurs de la STLItérateursAlgorithmes de la STLFoncteurs et expressions lambdaRécapitulatif - fréquence des mots13Modèles de conception, partie 1
Introduction aux modèles de conceptionModèle SingletonFabrique et fabrique abstraiteModèle BuilderModèle ObserverModèle Strategy2Gestion de la mémoire
Mémoire de pile ou de tasPointeurs et référencesMémoire dynamique (new/delete)Pointeurs intelligents en C++RAII en C++Récapitulatif - Gestionnaire de tableaux dynamiques5Encapsulation
Spécificateurs d’accès en C++Spécificateurs d’accès en profondeurMasquage de l’informationStruct vs classeClasses imbriquées et internesRécapitulatif - Système de gestion des dossiers étudiants8Polymorphisme
Polymorphisme à la compilation vs à l’exécutionSurcharge de fonctionsRetour sur les fonctions virtuellesFonctions virtuelles puresClasses abstraitesConception d’interfaces en C++Conversion dynamique et RTTIRécapitulatif - Calculateur de formes3Constructeurs et destructeurs
Constructeur par défautConstructeur paramétréConstructeur de copieConstructeur de déplacementListes d’initialisation des constructeursConstructeurs déléguésApprofondissement des destructeursRègle des trois / cinq / zéroRécapitulatif - Classe String6Surcharge des opérateurs
Introduction à la surcharge des opérateursSurcharge des opérateurs arithmétiquesSurcharge des opérateurs de comparaisonOpérateurs de fluxSurcharge de l’opérateur d’affectationSurcharge des opérateurs [] et ()Opérateurs de conversion de typeRécapitulatif – Classe Matrix9Templates
Templates de fonctionsTemplates de classesSpécialisation des templatesTemplates variadiquesBases de SFINAE et des traits de typesRécapitulatif - Conteneur génériqueEntraînez-vous par vous-même : Compilateur C++ en ligne