Classes aninhadas e internas
Parte da seção Programação Orientada a Objetos do Journey de C++ da Coddy. Lição 38 de 104.
Uma classe aninhada é uma classe definida dentro de outra classe. Essa técnica ajuda a organizar o código agrupando tipos relacionados e pode aprimorar o encapsulamento ao ocultar detalhes de implementação.
class LinkedList {
public:
class Node { // Classe aninhada
int data;
Node* next;
public:
Node(int d) : data(d), next(nullptr) {}
friend class LinkedList;
};
private:
Node* head;
public:
LinkedList() : head(nullptr) {}
};A classe aninhada Node é fortemente acoplada a LinkedList — ela só faz sentido nesse contexto. Ao aninhá-la, sinalizamos essa relação e evitamos poluir o namespace global com um nome genérico como "Node".
As classes aninhadas seguem as mesmas regras de acesso que você aprendeu. Uma classe aninhada declarada na seção privada só pode ser acessada dentro da classe externa.
Quando declarado como público, o código externo pode usá-lo com o operador de escopo:
LinkedList::Node node(42); // Somente se Node for públicoUm detalhe importante sobre a direção: uma classe aninhada é um membro da classe envolvente e tem os mesmos direitos de acesso que qualquer outro membro, portanto pode acessar os membros privados da classe envolvente sem uma declaração friend. O inverso não é automático, porém: a classe envolvente não tem acesso aos membros privados da classe aninhada.
É por isso que Node declara friend class LinkedList; no exemplo acima: a declaração friend concede ao LinkedList que o envolve acesso aos membros privados de Node, a única direção que não é automática.
Desafio
FácilVamos criar um sistema simples de gerenciamento de tarefas em que as tarefas são organizadas dentro de um projeto. Este é um cenário perfeito para classes aninhadas: uma Task só faz sentido no contexto de uma Project, então vamos aninhá-la dentro dela para manter nosso código organizado e evitar poluir o namespace global.
Você criará dois arquivos para organizar seu código:
Project.h: Defina uma classeProjectque contenha uma classe aninhadaTask.Sua classe aninhada
Taskdeve ser declarada na seção pública deProjectpara que possa ser acessada externamente. Ela deve armazenar um nome de tarefa (string) e um status de conclusão (bool). Dê a ela um construtor que receba o nome e defina a tarefa como incompleta por padrão. Inclua os métodoscomplete()para marcá-la como concluída,isComplete()para verificar seu status egetName()para obter o nome.A própria classe
Projectdeve armazenar um nome de projeto (string) e uma coleção de ponteiros paraTask. Forneça um construtor que receba o nome do projeto, um métodoaddTask(const std::string& taskName)que crie uma nova tarefa e a adicione ao projeto, um métodocompleteTask(const std::string& taskName)que encontre e conclua uma tarefa pelo nome e um métododisplayStatus()que mostre o nome do projeto seguido pelo status de cada tarefa.Como
Taskestá aninhada dentro deProject, a classeProjectpode trabalhar diretamente com as tarefas. Não se esqueça de liberar as tarefas alocadas dinamicamente no destrutor.main.cpp: Reúna tudo lendo um nome de projeto e dois nomes de tarefa da entrada (três linhas ao todo). Crie umaProjectcom o nome fornecido, adicione ambas as tarefas a ela e, em seguida, exiba o status inicial. Conclua a primeira tarefa e exiba o status novamente para mostrar a alteração.O formato de saída de
displayStatus()deve ser:Project: <project_name> - <task_name>: [incomplete] - <task_name>: [complete]Use
[complete]ou[incomplete]com base no status de cada tarefa.
Este desafio demonstra como as classes aninhadas ajudam a organizar tipos relacionados: a classe Task é fortemente acoplada a Project e não faria sentido por conta própria. Ao aninhá-la, sinalizamos claramente essa relação na estrutura do nosso código.
Experimente você mesmo
#include <iostream>
#include <string>
#include "Project.h"
using namespace std;
int main() {
// Ler entrada
string projectName;
string task1Name;
string task2Name;
getline(cin, projectName);
getline(cin, task1Name);
getline(cin, task2Name);
// TODO: Crie um Project com o nome fornecido
// TODO: Adicione ambas as tarefas ao projeto
// TODO: Exiba o status inicial
// TODO: Conclua a primeira tarefa
// TODO: Exiba o status novamente para mostrar a mudança
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