Menu
Coddy logo textTech

Palavra-chave Mutable

Parte da seção Programação Orientada a Objetos do Journey de C++ da Coddy — lição 30 de 104.

Você aprendeu que funções membros const não podem modificar nenhuma variável membro. Mas às vezes você precisa alterar um membro que não afeta o estado lógico do objeto - como um cache ou um contador de acesso. A palavra-chave mutable fornece uma exceção à restrição const.

Um membro mutable pode ser modificado mesmo dentro de uma função membro const:

class DataFetcher {
    std::string data;
    mutable int accessCount;    // Pode ser modificado em funções const
    
public:
    DataFetcher(std::string d) : data(d), accessCount(0) {}
    
    std::string getData() const {
        accessCount++;          // Permitido porque accessCount é mutable
        return data;
    }
    
    int getAccessCount() const {
        return accessCount;
    }
};

Sem o mutable, incrementar accessCount dentro da função const getData() causaria um erro de compilação. A palavra-chave mutable diz ao compilador: "Este membro tem permissão para mudar mesmo quando o objeto é considerado const."

Casos de uso comuns para mutable incluem o cache de valores computados, o rastreamento de estatísticas de acesso e primitivas de sincronização como mutexes. Em cada caso, a modificação não altera o que o objeto representa logicamente - um valor em cache é apenas uma otimização e um contador de acesso é metadado sobre o 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 com moderação - ele deve ser aplicado apenas a membros que não afetam o estado observável do objeto.

challenge icon

Desafio

Fácil

Vamos construir um visualizador de documentos que rastreia quantas vezes um documento foi visualizado — sem quebrar a const-correctness de nossos métodos de apenas leitura. Este é um caso de uso perfeito para a palavra-chave mutable.

Você criará dois arquivos para organizar seu código:

  • Document.h: Defina uma classe Document que armazena o conteúdo e rastreia secretamente as estatísticas de visualização. Sua classe precisa de:
    • Membros privados: title (string), content (string) e um mutable int viewCount que pode ser modificado mesmo em métodos const
    • Um construtor que recebe um título e conteúdo, inicializando viewCount como 0
    • Um método const getTitle() que retorna o título
    • Um método const getContent() que incrementa viewCount (permitido porque é mutable) e retorna o conteúdo
    • Um método const getViewCount() que retorna quantas vezes o conteúdo foi acessado
    • Um método const getPreview() que retorna os primeiros 20 caracteres do conteúdo seguidos por "..." — isso NÃO deve incrementar a contagem de visualizações, pois é apenas uma prévia
  • main.cpp: Demonstre como o mutable permite rastrear estatísticas de acesso enquanto mantém os métodos logicamente const. Leia o título e o conteúdo de um documento da entrada (cada um em uma linha separada) e, em seguida:
    • Crie um Document com os valores de entrada
    • Imprima "Title: <title>"
    • Imprima "Preview: <preview>"
    • Imprima "Views after preview: <count>"
    • Acesse o conteúdo completo usando getContent() e imprima "Content: <content>"
    • Imprima "Views after first read: <count>"
    • Acesse o conteúdo novamente e imprima "Content: <content>"
    • Imprima "Views after second read: <count>"
    • Crie uma função auxiliar void displayDocument(const Document& doc) que imprime "[Const Access] <title>: <content>" — isso prova que nossos métodos const funcionam com referências const
    • Chame displayDocument() com seu documento
    • Imprima "Final view count: <count>"

A contagem de visualizações deve ser 0 após a prévia (já que as prévias não contam como visualizações completas), e então incrementar cada vez que getContent() for chamado. A palavra-chave mutable torna isso possível — sem ela, você não poderia modificar viewCount dentro de métodos const como getContent().

Se o conteúdo for menor que 20 caracteres, getPreview() deve retornar todo o conteúdo seguido por "...".

Folha de consulta

A palavra-chave mutable permite que uma variável membro seja modificada mesmo dentro de funções membro const:

class DataFetcher {
    std::string data;
    mutable int accessCount;    // Pode ser modificada em funções const
    
public:
    DataFetcher(std::string d) : data(d), accessCount(0) {}
    
    std::string getData() const {
        accessCount++;          // Permitido porque accessCount é mutable
        return data;
    }
    
    int getAccessCount() const {
        return accessCount;
    }
};

Sem mutable, modificar uma variável membro dentro de uma função const causaria um erro de compilação. A palavra-chave diz ao compilador que este membro tem permissão para mudar mesmo quando o objeto é considerado const.

Casos de uso comuns incluem cache de valores calculados, rastreamento de estatísticas de acesso e primitivas de sincronização. A modificação não deve alterar o que o objeto representa logicamente:

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 com moderação - apenas para membros que não afetam o estado observável do objeto.

Experimente você mesmo

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

// TODO: Implementar a função displayDocument
// Deve imprimir: "[Const Access] <title>: <content>"
void displayDocument(const Document& doc) {
    // TODO: Implementar esta função
}

int main() {
    // Ler entrada
    string title;
    string content;
    getline(cin, title);
    getline(cin, content);
    
    // TODO: Criar um Document com os valores de entrada
    
    // TODO: Imprimir "Title: <title>"
    
    // TODO: Imprimir "Preview: <preview>"
    
    // TODO: Imprimir "Views after preview: <count>"
    
    // TODO: Acessar o conteúdo completo e imprimir "Content: <content>"
    
    // TODO: Imprimir "Views after first read: <count>"
    
    // TODO: Acessar o conteúdo novamente e imprimir "Content: <content>"
    
    // TODO: Imprimir "Views after second read: <count>"
    
    // TODO: Chamar displayDocument() com o seu documento
    
    // TODO: Imprimir "Final view count: <count>"
    
    return 0;
}
quiz iconTeste seus conhecimentos

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