Especificadores de acesso em profundidade
Parte da seção Programação Orientada a Objetos do Journey de C++ da Coddy. Lição 35 de 104.
Agora que você entende os três especificadores de acesso, vamos explorar como eles se comportam quando você tem vários objetos da mesma classe. Um equívoco comum é pensar que private significa “apenas este objeto” — mas, na verdade, significa “apenas esta classe”.
Objetos da mesma classe podem acessar os membros privados uns dos outros:
class Person {
std::string secret;
public:
Person(std::string s) : secret(s) {}
void readSecret(const Person& other) {
// Acessando o membro privado de outro objeto - permitido!
std::cout << other.secret;
}
};Isso funciona porque o controle de acesso é aplicado no nível da classe, não no nível do objeto. O método readSecret faz parte da classe Person, portanto pode acessar membros privados de qualquer objeto Person. Esse design permite que operações como construtores de cópia e operadores de comparação funcionem com o estado interno de outros objetos.
Você também pode alternar entre especificadores de acesso várias vezes dentro de uma classe:
class Mixed {
public:
void publicMethod1();
private:
int privateData;
public:
void publicMethod2(); // De volta ao público
private:
void privateHelper(); // Privado novamente
};Embora isso seja um C++ válido, geralmente é melhor agrupar os membros por nível de acesso para facilitar a leitura. A maioria dos guias de estilo recomenda listar primeiro os membros públicos (já que eles formam a interface da classe), seguidos pelos protegidos e, depois, pelos privados.
Desafio
FácilVamos criar um sistema de comparação de carteiras que demonstre como objetos da mesma classe podem acessar os membros privados uns dos outros. Esta é uma ideia fundamental sobre o controle de acesso do C++: ele é aplicado no nível da classe, não no nível do objeto.
Você criará dois arquivos para organizar seu código:
Wallet.h: defina uma classeWalletque armazene dados financeiros privados, mas permita comparações entre carteiras. Sua classe deve ter:- Membros privados:
ownerName(string) ebalance(double) - Um construtor que receba o nome do proprietário e o saldo inicial
- Um getter público
getOwnerName()que retorne o nome do proprietário - Um método
hasMoreThan(const Wallet& other)que compare o saldo desta carteira com o saldo privado de outra carteira, retornandotruese esta carteira tiver mais dinheiro - Um método
combinedBalance(const Wallet& other)que retorne a soma dos saldos privados das duas carteiras - Um método
transferTo(Wallet& other, double amount)que transfira dinheiro desta carteira para outra. Ele deve modificar diretamente os saldos privados das duas carteiras (somente se o valor for positivo e não exceder o saldo desta carteira)
- Membros privados:
main.cpp: demonstre o acesso a membros privados entre objetos. Leia dois nomes de proprietários e dois saldos da entrada (quatro linhas: name1, balance1, name2, balance2) e então:- Crie dois objetos
Walletcom os valores de entrada - Imprima
"Combined wealth: $<amount>"usandocombinedBalance() - Compare as carteiras e imprima
"<name> has more money"para o proprietário que tiver o maior saldo (usehasMoreThan()para determinar isso) - Transfira
100.0da primeira carteira para a segunda usandotransferTo() - Imprima
"After transfer:" - Imprima
"Combined wealth: $<amount>"novamente (o valor deve permanecer inalterado) - Compare novamente e imprima
"<name> has more money"(o resultado pode ter mudado)
- Crie dois objetos
O conceito fundamental aqui é que métodos como hasMoreThan() e transferTo() podem acessar diretamente o membro privado Wallet de outro objeto balance: não são necessários getters. Isso funciona porque o controle de acesso em C++ é baseado em classes: qualquer código dentro da classe Wallet pode acessar membros privados de qualquer objeto Wallet.
Formate todos os valores monetários com duas casas decimais usando std::fixed e std::setprecision(2) de <iomanip>. Converta as entradas de saldo usando std::stod().
Experimente você mesmo
#include <iostream>
#include <string>
#include <iomanip>
#include "Wallet.h"
using namespace std;
int main() {
// Ler entrada: name1, balance1, name2, balance2
string name1, name2;
string balanceStr1, balanceStr2;
getline(cin, name1);
getline(cin, balanceStr1);
getline(cin, name2);
getline(cin, balanceStr2);
double balance1 = stod(balanceStr1);
double balance2 = stod(balanceStr2);
// Definir formatação de saída para valores monetários
cout << fixed << setprecision(2);
// TODO: Criar dois objetos Wallet com os valores de entrada
// TODO: Print combined wealth using combinedBalance()
// Format: "Combined wealth: $<amount>"
// TODO: Compare wallets using hasMoreThan() and print who has more money
// Format: "<name> has more money"
// TODO: Transferir 100.0 da primeira carteira para a segunda usando transferTo()
// TODO: Print "After transfer:"
// TODO: Print combined wealth again
// TODO: Compare again and print who has more money
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