Clases anidadas e internas
Parte de la sección Programación Orientada a Objetos del Journey de C++ de Coddy — lección 38 de 104.
Una clase anidada es una clase definida dentro de otra clase. Esta técnica ayuda a organizar el código agrupando tipos relacionados y puede mejorar el encapsulamiento al ocultar los detalles de implementación.
class LinkedList {
public:
class Node { // Clase anidada
int data;
Node* next;
public:
Node(int d) : data(d), next(nullptr) {}
friend class LinkedList;
};
private:
Node* head;
public:
LinkedList() : head(nullptr) {}
};La clase anidada Node está estrechamente acoplada a LinkedList - solo tiene sentido en ese contexto. Al anidarla, señalamos esta relación y evitamos contaminar el espacio de nombres global con un nombre genérico como "Node".
Las clases anidadas siguen las mismas reglas de acceso que has aprendido. Una clase anidada declarada en la sección private solo es accesible dentro de la clase externa.
Cuando se declara como público, el código externo puede usarlo con el operador de ámbito:
LinkedList::Node node(42); // Solo si Node es públicoUn detalle importante: la clase anidada no tiene acceso automáticamente a los miembros privados de la clase externa. Son clases separadas que simplemente comparten un espacio de nombres.
Si la clase anidada necesita acceso, debes declararla como friend, como se muestra en el ejemplo anterior.
Desafío
FácilVamos a construir un sistema simple de gestión de tareas donde las tareas se organizan dentro de un proyecto. Este es un escenario perfecto para las clases anidadas: una Task solo tiene sentido dentro del contexto de un Project, por lo que la anidaremos dentro para mantener nuestro código organizado y evitar contaminar el espacio de nombres global.
Crearás dos archivos para organizar tu código:
Project.h: Define una claseProjectque contenga una claseTaskanidada.Tu clase
Taskanidada debe declararse en la sección public deProjectpara que se pueda acceder a ella desde el exterior. Debe almacenar un nombre de tarea (string) y un estado de finalización (bool). Dale un constructor que reciba el nombre y establezca la tarea como incompleta por defecto. Incluye los métodoscomplete()para marcarla como terminada,isComplete()para verificar su estado ygetName()para recuperar el nombre.La clase
Projecten sí debe almacenar un nombre de proyecto (string) y una colección de punteros aTask. Proporciona un constructor que reciba el nombre del proyecto, un métodoaddTask(const std::string& taskName)que cree una nueva tarea y la añada al proyecto, un métodocompleteTask(const std::string& taskName)que busque y complete una tarea por su nombre, y un métododisplayStatus()que muestre el nombre del proyecto seguido del estado de cada tarea.Dado que
Taskestá anidada dentro deProject, la claseProjectpuede trabajar con las tareas directamente. No olvides limpiar las tareas asignadas dinámicamente en el destructor.main.cpp: Une todo leyendo un nombre de proyecto y dos nombres de tareas de la entrada (tres líneas en total). Crea unProjectcon el nombre dado, añádele ambas tareas y luego muestra el estado inicial. Completa la primera tarea y muestra el estado de nuevo para mostrar el cambio.El formato de salida para
displayStatus()debe ser:Project: <project_name> - <task_name>: [incomplete] - <task_name>: [complete]Usa
[complete]o[incomplete]basándote en el estado de cada tarea.
Este desafío demuestra cómo las clases anidadas ayudan a organizar tipos relacionados: la clase Task está estrechamente ligada a Project y no tendría sentido por sí sola. Al anidarla, señalamos esta relación claramente en nuestra estructura de código.
Hoja de referencia
Una clase anidada es una clase definida dentro de otra clase. Ayuda a organizar el código agrupando tipos relacionados y mejora el encapsulamiento.
class LinkedList {
public:
class Node { // Clase anidada
int data;
Node* next;
public:
Node(int d) : data(d), next(nullptr) {}
friend class LinkedList;
};
private:
Node* head;
public:
LinkedList() : head(nullptr) {}
};Las clases anidadas siguen las reglas de acceso estándar. Una clase anidada en la sección privada solo es accesible dentro de la clase externa. Cuando se declara pública, el código externo puede usarla con el operador de ámbito:
LinkedList::Node node(42); // Solo si Node es públicaLa clase anidada no tiene acceso automáticamente a los miembros privados de la clase externa. Si se necesita acceso, declárela como amiga (friend):
friend class LinkedList;Pruébalo tú mismo
#include <iostream>
#include <string>
#include "Project.h"
using namespace std;
int main() {
// Leer la entrada
string projectName;
string task1Name;
string task2Name;
getline(cin, projectName);
getline(cin, task1Name);
getline(cin, task2Name);
// TODO: Crear un Project con el nombre dado
// TODO: Añadir ambas tareas al proyecto
// TODO: Mostrar el estado inicial
// TODO: Completar la primera tarea
// TODO: Mostrar el estado de nuevo para ver el cambio
return 0;
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de OOP
Archivos externosConstrucción y compilación en C++Archivos de cabecera y archivos fuenteNamespaces y alcanceIntroducción a OOP en C++Clases vs ObjetosEl puntero 'this'Métodos (Funciones miembro)Atributos (Miembros de datos)Conceptos básicos de Ctors y DtorsResumen - Calculadora simple4Propiedades de clase
Miembros de instancia vs. estáticosGetters y SettersFunciones miembro constPalabra clave mutableMétodos y variables estáticosFunciones y clases amigasResumen - Gestor de cuentas bancarias7Herencia
Herencia básicaNiveles de acceso en la herenciaOrden de llamada de Ctor y DtorSobrescritura de métodosFunciones virtuales y VTableHerencia múltipleHerencia virtualResumen - Jerarquía de empleados2Gestión de memoria
Memoria Stack vs HeapPunteros y referenciasMemoria dinámica (new/delete)Punteros inteligentes en C++RAII en C++Resumen - Gestor de arrays dinámicos5Encapsulamiento
Especificadores de acceso en C++Especificadores de acceso en profundidadOcultamiento de informaciónStruct vs ClassClases anidadas e internasResumen - Sistema de registros de estudiantes8Polimorfismo
Polimorfismo: Compilación vs. Tiempo de ejecuciónSobrecarga de funcionesFunciones virtuales revisadasFunciones virtuales purasClases abstractasDiseño de interfaces en C++Dynamic Casting y RTTIResumen: Calculadora de figuras11Conceptos avanzados de POO
Composición vs. HerenciaMixins mediante CRTPIdioma PimplBorrado de tiposEnum Classes y tipado fuerteManejo de excepciones en POOJerarquías de excepciones personalizadas14Patrones de diseño - Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositeRAII como patrón3Constructores y Destructores
Constructor por defectoConstructor parametrizadoConstructor de copiaConstructor de movimientoListas de inicialización del constructorConstructores delegadosAnálisis profundo del destructorRegla de tres / cinco / ceroResumen - Clase String6Sobrecarga de operadores
Introducción a la sobrecarga de operadoresSobrecarga de operadores aritméticosSobrecarga de operadores de comparaciónOperadores de flujo (Stream)Sobrecarga del operador de asignaciónSobrecarga de los operadores [] y ()Operadores de conversión de tiposResumen - Clase Matrix9Plantillas
Plantillas de funcionesPlantillas de clasesEspecialización de plantillasPlantillas variádicasConceptos básicos de SFINAE y Type TraitsResumen - Contenedor genérico