Níveis de acesso da herança
Parte da seção Programação Orientada a Objetos do Journey de C++ da Coddy. Lição 49 de 104.
O tipo de herança que você escolhe afeta como os membros da classe base são acessados na classe derivada. O C++ oferece três opções: herança public, protected e private.
Veja como cada tipo transforma o acesso aos membros:
| Membro da classe base | herança pública | herança protegida | herança privada |
|---|---|---|---|
| público | público | protegido | privado |
| protegido | protegido | protegido | privado |
| privado | inacessível | inacessível | inacessível |
class Base {
public:
int pub;
protected:
int prot;
private:
int priv;
};
class PublicDerived : public Base {
// pub é public, prot é protected, priv é inacessível
};
class ProtectedDerived : protected Base {
// pub é protected, prot é protected, priv é inacessível
};
class PrivateDerived : private Base {
// pub é private, prot é private, priv é inacessível
};Herança pública modela relações de “é um” e é de longe a mais comum. Um Dog é um Animal, portanto, o código externo pode tratá-lo como tal.
Herança protegida e privada modela relacionamentos de “implementado em termos de”. Elas ocultam a interface da classe base do código externo, sendo úteis quando você quer reutilizar a implementação sem expor o relacionamento de herança. Com a herança privada, até mesmo as classes derivadas posteriormente perdem o acesso aos membros da classe base original.
Desafio
FácilVamos explorar como diferentes tipos de herança afetam a acessibilidade dos membros construindo uma hierarquia simples de dispositivos. Você criará uma classe base Device e três classes derivadas, cada uma usando um tipo diferente de herança, para ver na prática como as heranças pública, protegida e privada transformam o acesso aos membros.
Você organizará seu código em dois arquivos:
Device.h: defina uma classe baseDevicecom três membros em diferentes níveis de acesso:- Uma
std::string modelpública - Um
int serialNumberprotegido - Um
int internalCodeprivado (definido como 999 por padrão) - Um método público
showModel()que imprime:Model: <model>
Em seguida, crie três classes derivadas, cada uma herdando de
Deviceusando um tipo diferente de herança:PublicDeviceusando herança pública: adicione um métododisplaySerial()que imprime:Serial: <serialNumber>ProtectedDeviceusando herança protegida: adicione um métododisplaySerial()que imprime:Serial: <serialNumber>PrivateDeviceusando herança privada: adicione um métododisplaySerial()que imprime:Serial: <serialNumber>
Cada classe derivada deve ter um construtor que receba um nome de modelo e um número de série, passando o modelo para a classe base e definindo o número de série.
- Uma
main.cpp: leia duas entradas (cada uma em uma linha separada):- Nome do modelo (string)
- Número de série (inteiro)
Crie um objeto de cada tipo derivado com os mesmos valores de entrada. Em seguida, demonstre as diferenças de acesso:
Para
PublicDevice:- Chame
showModel()diretamente no objeto (funciona porque a herança pública o mantém público) - Acesse
modeldiretamente e imprima:Direct model access: <model> - Chame
displaySerial()
Para
ProtectedDeviceePrivateDevice:- Chame apenas
displaySerial()(oshowModel()herdado emodelnão estão mais publicamente acessíveis de fora)
Formato da saída:
--- PublicDevice --- Model: <model> Direct model access: <model> Serial: <serial> --- ProtectedDevice --- Serial: <serial> --- PrivateDevice --- Serial: <serial>
Por exemplo, com as entradas Laptop e 12345:
--- PublicDevice ---
Model: Laptop
Direct model access: Laptop
Serial: 12345
--- ProtectedDevice ---
Serial: 12345
--- PrivateDevice ---
Serial: 12345Observe como apenas PublicDevice permite acesso externo aos membros públicos herdados. Os outros tipos de herança ocultam a interface da classe base do código externo, mas as classes derivadas ainda podem acessar membros protegidos internamente por meio de seus próprios métodos.
Experimente você mesmo
#include <iostream>
#include <string>
#include "Device.h"
using namespace std;
int main() {
// Ler entrada
string modelName;
int serialNum;
getline(cin, modelName);
cin >> serialNum;
// TODO: Crie objetos de cada tipo derivado com os valores de entrada
// PublicDevice pubDev(modelName, serialNum);
// ProtectedDevice protDev(modelName, serialNum);
// PrivateDevice privDev(modelName, serialNum);
// TODO: Demonstrate PublicDevice access
cout << "--- PublicDevice ---" << endl;
// - Chame showModel() diretamente no objeto
// - Access model directly and print: Direct model access: <model>
// - Chame displaySerial()
// TODO: Demonstrate ProtectedDevice access
cout << "--- ProtectedDevice ---" << endl;
// - Chame apenas displaySerial() (showModel e model não são acessíveis publicamente)
// TODO: Demonstrate PrivateDevice access
cout << "--- PrivateDevice ---" << endl;
// - Chame apenas displaySerial() (showModel e model não são acessíveis publicamente)
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