Menu
Coddy logo textTech

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 la encapsulación 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 privada 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 public

Un detalle importante sobre la dirección: una class anidada es un miembro de la class contenedora y tiene los mismos derechos de acceso que cualquier otro miembro, por lo que puede acceder a los miembros privados de la class contenedora sin una declaración friend. Sin embargo, lo contrario no es automático: la class contenedora no tiene acceso a los miembros privados de la class anidada.

Por eso Node declara friend class LinkedList; en el ejemplo anterior: la declaración friend otorga a la LinkedList que la contiene acceso a los miembros privados de Node, la única dirección que no es automática.

challenge icon

Desafío

Fácil

Construyamos un sistema sencillo de gestión de tareas en el que las tareas estén organizadas dentro de un proyecto. Este es un escenario perfecto para las clases anidadas: una Task solo tiene sentido en el contexto de una Project, así 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 clase Project que contiene una clase Task anidada.

    Tu clase Task anidada debe declararse en la sección public de Project para que se pueda acceder a ella desde fuera. Debe almacenar un nombre de tarea (cadena) y un estado de finalización (booleano). Asígnale un constructor que reciba el nombre y establezca la tarea como incompleta de forma predeterminada. Incluye los métodos complete() para marcarla como completada, isComplete() para comprobar su estado y getName() para recuperar el nombre.

    La propia clase Project debe almacenar un nombre de proyecto (cadena) y una colección de punteros a Task. Proporciona un constructor que reciba el nombre del proyecto, un método addTask(const std::string& taskName) que cree una tarea nueva y la añada al proyecto, un método completeTask(const std::string& taskName) que busque y complete una tarea por nombre, y un método displayStatus() que muestre el nombre del proyecto seguido del estado de cada tarea.

    Como Task está anidada dentro de Project, la clase Project puede trabajar directamente con las tareas. No olvides liberar las tareas asignadas dinámicamente en el destructor.

  • main.cpp: Une todos los elementos leyendo un nombre de proyecto y dos nombres de tarea de la entrada (tres líneas en total). Crea un Project con el nombre proporcionado, añade ambas tareas y después muestra el estado inicial. Completa la primera tarea y vuelve a mostrar el estado para ver el cambio.

    El formato de salida de displayStatus() debe ser:

    Project: <project_name>
      - <task_name>: [incomplete]
      - <task_name>: [complete]

    Usa [complete] o [incomplete] según el estado de cada tarea.

Este desafío demuestra cómo las clases anidadas ayudan a organizar tipos relacionados: la clase Task está estrechamente vinculada a Project y no tendría sentido por sí sola. Al anidarla, indicamos claramente esta relación en la estructura de nuestro código.

Pruébalo tú mismo

#include <iostream>
#include <string>
#include "Project.h"

using namespace std;

int main() {
    // Leer entrada
    string projectName;
    string task1Name;
    string task2Name;
    
    getline(cin, projectName);
    getline(cin, task1Name);
    getline(cin, task2Name);
    
    // TODO: Crea un Project con el nombre dado
    
    // TODO: Añade ambas tareas al proyecto
    
    // TODO: Muestra el estado inicial
    
    // TODO: Completa la primera tarea
    
    // TODO: Muestra el estado de nuevo para mostrar el cambio
    
    return 0;
}
quiz iconPonte a prueba

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

Practica por tu cuenta: Compilador de C++ online