Menu
Coddy logo textTech

Palabra clave mutable

Parte de la sección Programación Orientada a Objetos del Journey de C++ de Coddy — lección 30 de 104.

Has aprendido que las funciones miembro const no pueden modificar ninguna variable miembro. Pero a veces necesitas cambiar un miembro que no afecta al estado lógico del objeto, como una caché o un contador de acceso. La palabra clave mutable proporciona una excepción a la restricción const.

Un miembro mutable puede ser modificado incluso dentro de una función miembro const:

class DataFetcher {
    std::string data;
    mutable int accessCount;    // Puede ser modificado en funciones const
    
public:
    DataFetcher(std::string d) : data(d), accessCount(0) {}
    
    std::string getData() const {
        accessCount++;          // Permitido porque accessCount es mutable
        return data;
    }
    
    int getAccessCount() const {
        return accessCount;
    }
};

Sin mutable, incrementar accessCount dentro de la función const getData() causaría un error de compilación. La palabra clave mutable le dice al compilador: "Se permite que este miembro cambie incluso cuando el objeto se considera const."

Los casos de uso comunes para mutable incluyen el almacenamiento en caché de valores calculados, el seguimiento de estadísticas de acceso y primitivas de sincronización como mutexes. En cada caso, la modificación no cambia lo que el objeto representa lógicamente: un valor almacenado en caché es solo una optimización y un contador de acceso son metadatos sobre el uso.

class Circle {
    double radius;
    mutable double cachedArea;
    mutable bool areaCached;
    
public:
    double getArea() const {
        if (!areaCached) {
            cachedArea = 3.14159 * radius * radius;
            areaCached = true;
        }
        return cachedArea;
    }
};

Use mutable con moderación; solo debe aplicarse a miembros que no afecten al estado observable del objeto.

challenge icon

Desafío

Fácil

Vamos a construir un visor de documentos que rastrea cuántas veces se ha visto un documento, sin romper la corrección de const (const-correctness) de nuestros métodos de solo lectura. Este es un caso de uso perfecto para la palabra clave mutable.

Crearás dos archivos para organizar tu código:

  • Document.h: Define una clase Document que almacena el contenido y rastrea secretamente las estadísticas de visualización. Tu clase necesita:
    • Miembros privados: title (string), content (string), y un mutable int viewCount que pueda ser modificado incluso en métodos const
    • Un constructor que reciba un título y contenido, inicializando viewCount en 0
    • Un método const getTitle() que devuelva el título
    • Un método const getContent() que incremente viewCount (permitido porque es mutable) y devuelva el contenido
    • Un método const getViewCount() que devuelva cuántas veces se ha accedido al contenido
    • Un método const getPreview() que devuelva los primeros 20 caracteres del contenido seguidos de "..." — esto NO debe incrementar el contador de visitas ya que es solo una vista previa
  • main.cpp: Demuestra cómo mutable permite rastrear estadísticas de acceso manteniendo los métodos lógicamente const. Lee el título y el contenido de un documento desde la entrada (cada uno en una línea separada), luego:
    • Crea un Document con los valores de entrada
    • Imprime "Title: <title>"
    • Imprime "Preview: <preview>"
    • Imprime "Views after preview: <count>"
    • Accede al contenido completo usando getContent() e imprime "Content: <content>"
    • Imprime "Views after first read: <count>"
    • Accede al contenido de nuevo e imprime "Content: <content>"
    • Imprime "Views after second read: <count>"
    • Crea una función auxiliar void displayDocument(const Document& doc) que imprima "[Const Access] <title>: <content>" — esto demuestra que nuestros métodos const funcionan con referencias const
    • Llama a displayDocument() con tu documento
    • Imprime "Final view count: <count>"

El contador de visitas debe ser 0 después de la vista previa (ya que las vistas previas no cuentan como visitas completas), luego incrementarse cada vez que se llame a getContent(). La palabra clave mutable hace esto posible — sin ella, no podrías modificar viewCount dentro de métodos const como getContent().

Si el contenido tiene menos de 20 caracteres, getPreview() debe devolver todo el contenido seguido de "...".

Hoja de referencia

La palabra clave mutable permite que una variable miembro sea modificada incluso dentro de funciones miembro const:

class DataFetcher {
    std::string data;
    mutable int accessCount;    // Can be modified in const functions
    
public:
    DataFetcher(std::string d) : data(d), accessCount(0) {}
    
    std::string getData() const {
        accessCount++;          // Allowed because accessCount is mutable
        return data;
    }
    
    int getAccessCount() const {
        return accessCount;
    }
};

Sin mutable, modificar una variable miembro dentro de una función const causaría un error de compilación. La palabra clave le indica al compilador que este miembro tiene permitido cambiar incluso cuando el objeto se considera const.

Los casos de uso comunes incluyen el almacenamiento en caché de valores calculados, el seguimiento de estadísticas de acceso y las primitivas de sincronización. La modificación no debe cambiar lo que el objeto representa lógicamente:

class Circle {
    double radius;
    mutable double cachedArea;
    mutable bool areaCached;
    
public:
    double getArea() const {
        if (!areaCached) {
            cachedArea = 3.14159 * radius * radius;
            areaCached = true;
        }
        return cachedArea;
    }
};

Use mutable con moderación, solo para miembros que no afecten el estado observable del objeto.

Pruébalo tú mismo

#include <iostream>
#include <string>
#include "Document.h"
using namespace std;

// TODO: Implementar la función displayDocument
// Debe imprimir: "[Const Access] <title>: <content>"
void displayDocument(const Document& doc) {
    // TODO: Implementar esta función
}

int main() {
    // Leer la entrada
    string title;
    string content;
    getline(cin, title);
    getline(cin, content);
    
    // TODO: Crear un Document con los valores de entrada
    
    // TODO: Imprimir "Title: <title>"
    
    // TODO: Imprimir "Preview: <preview>"
    
    // TODO: Imprimir "Views after preview: <count>"
    
    // TODO: Acceder al contenido completo e imprimir "Content: <content>"
    
    // TODO: Imprimir "Views after first read: <count>"
    
    // TODO: Acceder al contenido de nuevo e imprimir "Content: <content>"
    
    // TODO: Imprimir "Views after second read: <count>"
    
    // TODO: Llamar a displayDocument() con tu documento
    
    // TODO: Imprimir "Final view count: <count>"
    
    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