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. Esta técnica ajuda a organizar o código agrupando tipos relacionados e pode melhorar 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 está fortemente acoplada à LinkedList - ela só faz sentido nesse contexto. Ao aninhá-la, sinalizamos esse relacionamento e evitamos poluir o namespace global com um nome genérico como "Node".
Classes aninhadas seguem as mesmas regras de acesso que você aprendeu. Uma classe aninhada declarada na seção private é acessível apenas dentro da classe externa.
Quando declarado como public, o código externo pode usá-lo com o operador de escopo:
LinkedList::Node node(42); // Apenas se Node for públicoUm detalhe importante: a classe aninhada não tem acesso automático aos membros privados da classe externa. Elas são classes separadas que por acaso compartilham um namespace.
Se a classe aninhada precisar de acesso, você deve declará-la como friend, como mostrado no exemplo acima.
Desafio
FácilVamos construir um sistema simples de gerenciamento de tarefas onde as tarefas são organizadas dentro de um projeto. Este é um cenário perfeito para classes aninhadas — uma Task só faz sentido dentro do contexto de um Project, então vamos aninhá-la internamente 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 classeTaskaninhada.Sua classe
Taskaninhada deve ser declarada na seção public deProjectpara que possa ser acessada de fora. Ela deve armazenar um nome de tarefa (string) e um status de conclusão (bool). Forneça 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 recuperar o nome.A classe
Projectem si deve 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 cria uma nova tarefa e a adiciona ao projeto, um métodocompleteTask(const std::string& taskName)que encontra e conclui uma tarefa pelo nome, e um métododisplayStatus()que mostra o nome do projeto seguido pelo status de cada tarefa.Como
Taskestá aninhada dentro deProject, a classeProjectpode trabalhar com as tarefas diretamente. Não se esqueça de limpar as tarefas alocadas dinamicamente no destruidor.main.cpp: Reúna tudo lendo um nome de projeto e dois nomes de tarefas da entrada (três linhas no total). Crie umProjectcom o nome fornecido, adicione ambas as tarefas a ele 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 para
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 classes aninhadas ajudam a organizar tipos relacionados — a classe Task está fortemente acoplada ao Project e não faria sentido por si só. Ao aninhá-la, sinalizamos esse relacionamento claramente em nossa estrutura de código.
Folha de consulta
Uma classe aninhada é uma classe definida dentro de outra classe. Ela ajuda a organizar o código agrupando tipos relacionados e melhora o encapsulamento.
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) {}
};Classes aninhadas seguem as regras de acesso padrão. Uma classe aninhada na seção privada é acessível apenas dentro da classe externa. Quando declarada como pública, o código externo pode usá-la com o operador de escopo:
LinkedList::Node node(42); // Apenas se Node for públicaA classe aninhada não tem acesso automático aos membros privados da classe externa. Se o acesso for necessário, declare-a como amiga (friend):
friend class LinkedList;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: Criar um Projeto com o nome fornecido
// TODO: Adicionar ambas as tarefas ao projeto
// TODO: Exibir o status inicial
// TODO: Concluir a primeira tarefa
// TODO: Exibir o status novamente para mostrar a alteração
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 OOP
Arquivos ExternosBuild e Compilação em C++Arquivos Header e SourceNamespaces e EscopoIntrodução a OOP em C++Classes vs ObjetosO Ponteiro 'this'Métodos (Funções Membro)Atributos (Membros de Dados)Básico de Ctors e DtorsRecapitulação - Calculadora Simples4Propriedades de Classe
Membros de Instância vs EstáticosGetters e SettersFunções de Membro ConstPalavra-chave MutableMétodos e Variáveis EstáticosFunções e Classes FriendRecapitulação - Gerenciador de Conta Bancária7Herança
Herança BásicaNíveis de Acesso na HerançaOrdem de Chamada de Ctor e DtorSobrescrita de MétodosFunções Virtuais e VTableHerança MúltiplaHerança VirtualRecapitulação - Hierarquia de Funcionários2Gerenciamento de Memória
Memória Stack vs HeapPonteiros e ReferênciasMemória Dinâmica (new/delete)Smart Pointers em C++RAII em C++Recapitulação - Gerenciador de Array Dinâmico5Encapsulamento
Especificadores de Acesso em C++Especificadores de Acesso em DetalhesOcultação de InformaçãoStruct vs ClassClasses Aninhadas e InternasRecapitulação - Sistema de Registros de Alunos8Polimorfismo
Polimorfismo de Compilação vs Tempo de ExecuçãoSobrecarga de FunçõesFunções Virtuais RevisitadasFunções Virtuais PurasClasses AbstratasDesign de Interfaces em C++Dynamic Casting e RTTIRecapitulação - Calculadora de Formas3Construtores e Destrutores
Construtor PadrãoConstrutor ParametrizadoConstrutor de CópiaConstrutor de MovimentaçãoListas de Inicialização do ConstrutorConstrutores DelegadosMergulho Profundo em DestrutoresRegra dos Três / Cinco / ZeroRecapitulação - Classe String6Sobrecarga de Operadores
Introdução à Sobrecarga de OperadoresSobrecarga de Operadores AritméticosSobrecarga de Operadores de ComparaçãoOperadores de StreamSobrecarga do Operador de AtribuiçãoSobrecarga dos Operadores [] e ()Operadores de Conversão de TipoRecapitulação - Classe Matrix9Templates
Templates de FunçãoTemplates de ClasseEspecialização de TemplatesTemplates VariádicosFundamentos de SFINAE e Type TraitsRecapitulação - Container Genérico