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.
Desafio
FácilVamos 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 classeDocumentque armazena o conteúdo e rastreia secretamente as estatísticas de visualização. Sua classe precisa de:- Membros privados:
title(string),content(string) e ummutable int viewCountque pode ser modificado mesmo em métodos const - Um construtor que recebe um título e conteúdo, inicializando
viewCountcomo0 - Um método const
getTitle()que retorna o título - Um método const
getContent()que incrementaviewCount(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
- Membros privados:
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
Documentcom 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>"
- Crie um
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;
}
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