Funções e Classes Friend
Parte da seção Programação Orientada a Objetos do Journey de C++ da Coddy. Lição 32 de 104.
Às vezes, uma função ou classe externa à sua classe precisa acessar seus membros privados. Em vez de tornar esses membros públicos, o C++ fornece a palavra-chave friend para conceder acesso seletivo mantendo o encapsulamento.
Uma função friend é declarada dentro de uma classe, mas não é membro dessa classe. Ela pode acessar os membros privados e protegidos da classe:
class Box {
double width;
public:
Box(double w) : width(w) {}
// Declare printWidth como friend
friend void printWidth(const Box& b);
};
// Definição de função friend - NÃO é uma função membro
void printWidth(const Box& b) {
std::cout << b.width; // Pode acessar membro privado
}Observe que printWidth é definido fora da classe sem o operador de resolução de escopo. É uma função comum que, por acaso, tem privilégios de acesso especiais.
Você também pode tornar uma classe inteira uma amiga, concedendo a todas as suas funções membro acesso aos membros privados:
class Engine {
int horsepower;
friend class Mechanic; // Mechanic pode acessar membros privados
public:
Engine(int hp) : horsepower(hp) {}
};
class Mechanic {
public:
void diagnose(const Engine& e) {
std::cout << e.horsepower; // Permitido - Mechanic é um friend
}
};A amizade não é mútua — se Engine declara Mechanic como amigo, Mechanic pode acessar os members privados de Engine, mas o contrário não é válido. Use amigos com moderação, pois eles criam um forte acoplamento entre as classes. Eles são mais úteis para sobrecarga de operator e funções auxiliares estreitamente relacionadas.
Desafio
FácilVamos criar um sistema de mensagens seguro em que uma classe MessageReader precisa de acesso especial para ler mensagens criptografadas armazenadas em uma classe SecureMessage. Este é um cenário perfeito para usar funções amigas e classes amigas.
Você criará dois arquivos para organizar seu código:
SecureMessage.h: Defina uma classeSecureMessageque armazene dados privados da mensagem e conceda acesso seletivo a entidades confiáveis. Sua classe deve ter:- Membros privados:
content(string) esecretKey(int) - Um construtor que receba o conteúdo e a chave secreta
- Declare
MessageReadercomo uma classe amiga para que ela possa acessar os membros privados - Declare uma função amiga
decryptMessage(const SecureMessage& msg, int key)que retorne o conteúdo somente se a chave fornecida corresponder à chave secreta; caso contrário, retorne"Access Denied"
Em seguida, defina a classe
MessageReaderno mesmo arquivo de cabeçalho:- Um método
readContent(const SecureMessage& msg)que acesse diretamente e retorne ocontentprivado da mensagem - Um método
verifyKey(const SecureMessage& msg, int key)que verifique se a chave fornecida corresponde àsecretKeyda mensagem e retorne"Valid"ou"Invalid"
Por fim, defina a função amiga
decryptMessageapós as classes.- Membros privados:
main.cpp: Demonstre as funções amigas e as classes amigas em ação. Leia o conteúdo de uma mensagem, uma chave secreta e uma chave de teste da entrada (cada um em uma linha separada) e, em seguida:- Crie uma
SecureMessagecom o conteúdo e a chave secreta - Crie um objeto
MessageReader - Imprima
"Reader access: <content>"usando o métodoreadContent()do leitor - Imprima
"Key verification: <result>"usandoverifyKey()com a chave de teste - Imprima
"Decrypt with test key: <result>"usando a função amiga com a chave de teste - Imprima
"Decrypt with correct key: <result>"usando a função amiga com a chave secreta original
- Crie uma
Lembre-se de que as funções amigas são definidas fora da classe sem o operador de resolução de escopo: elas são funções comuns com privilégios especiais de acesso. A classe MessageReader, por ser amiga, pode acessar diretamente os membros privados de SecureMessage em todos os seus métodos.
Converta as entradas de chave de strings para inteiros usando std::stoi(). Inclua seu arquivo de cabeçalho em main.cpp usando #include "SecureMessage.h".
Experimente você mesmo
#include <iostream>
#include <string>
#include "SecureMessage.h"
using namespace std;
int main() {
// Ler 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: Crie um SecureMessage com o conteúdo e a chave secreta
// TODO: Crie um objeto 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;
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de POO
Arquivos externosBuild e compilação em C++Arquivos de cabeçalho e arquivos-fonteNamespaces e escopoIntrodução à POO em C++Classes vs. objetosO ponteiro 'this'Métodos (funções membro)Atributos (membros de dados)Noções básicas de construtores e destruidoresRevisão - calculadora simples4Propriedades de Classe
Membros de Instância vs. EstáticosGetters e SettersFunções Membro ConstPalavra-chave MutableMétodos e Variáveis EstáticosFunções e Classes FriendRevisão - Gerenciador de Contas Bancárias7Herança
Herança básicaNíveis de acesso da herançaOrdem de chamada de construtor e destrutorSobrescrita de métodosFunções virtuais e VTableHerança múltiplaHerança virtualRevisão - Hierarquia de funcionários10Visão geral da STL
Visão geral e filosofia da STLContêineres da STLIteradoresAlgoritmos da STLFunctors e expressões lambdaRecapitulação - Frequência de palavras13Padrões de Projeto — Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonFactory e Abstract FactoryPadrão BuilderPadrão ObserverPadrão Strategy2Gerenciamento de Memória
Memória Stack vs. HeapPonteiros e ReferênciasMemória Dinâmica (new/delete)Ponteiros Inteligentes em C++RAII em C++Revisão - Gerenciador de Array Dinâmico5Encapsulamento
Especificadores de acesso em C++Especificadores de acesso em profundidadeOcultação de informaçõesStruct vs ClasseClasses aninhadas e internasRecapitulação - Sistema de registros de alunos8Polimorfismo
Polimorfismo em compilação vs. execuçãoSobrecarga de funçõesFunções virtuais revisadasFunções virtuais purasClasses abstratasDesign de interfaces em C++Conversão dinâmica e RTTIRecapitulação — Calculadora de formas11Conceitos Avançados de OOP
Composição vs. HerançaMixins via CRTPIdioma PimplApagamento de TiposClasses Enum e Tipagem ForteTratamento de Exceções em OOPHierarquias de Exceções Personalizadas14Padrões de Projeto — Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositeRAII como Padrão3Construtores e Destrutores
Construtor PadrãoConstrutor ParametrizadoConstrutor de CópiaConstrutor de MovimentoListas de Inicialização do ConstrutorConstrutores DelegantesAprofundamento em DestrutoresRegra dos Três / Cinco / ZeroRevisão - Classe String6Sobrecarga de Operadores
Introdução à Sobrecarga de OperadoresSobrecarga de Operadores AritméticosSobrecarga de Operadores de ComparaçãoOperadores de FluxoSobrecarga do Operador de AtribuiçãoSobrecarga dos Operadores [] e ()Operadores de Conversão de TipoRecapitulação - Classe Matrix9Templates
Templates de FunçõesTemplates de ClassesEspecialização de TemplatesTemplates VariádicosNoções Básicas de SFINAE e Traits de TiposRecapitulação - Contêiner GenéricoPratique por conta própria: Compilador de C++ online