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 membro 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 acessos. A palavra-chave mutable fornece uma exceção à restrição de const.
Um membro mutável 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 mutable, incrementar accessCount dentro da função const getData() causaria um erro de compilação. A palavra-chave mutable diz ao compilador: "Este membro pode ser alterado mesmo quando o objeto é considerado const."
Casos de uso comuns para mutable incluem armazenar em cache valores calculados, acompanhar 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 armazenado 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 criar um visualizador de documentos que rastreia quantas vezes um documento foi visualizado, sem quebrar a correção de constância dos nossos métodos somente para 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 discretamente as estatísticas de visualização. Sua classe precisa de:- Membros privados:
title(string),content(string) e ummutable int viewCountque pode ser modificado até mesmo em métodos const - Um construtor que recebe um título e um conteúdo, inicializando
viewCountcom0 - Um método const
getTitle()que retorna o título - Um método const
getContent()que incrementaviewCount(permitido porque ele é 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 20 primeiros 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 mutable permite rastrear estatísticas de acesso mantendo os métodos logicamente const. Leia um título e um conteúdo de 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 comprova 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, em seguida, ser incrementada 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 tiver menos de 20 caracteres, getPreview() deverá retornar todo o conteúdo seguido por "...".
Experimente você mesmo
#include <iostream>
#include <string>
#include "Document.h"
using namespace std;
// TODO: Implemente a função displayDocument
// Deve imprimir: "[Const Access] <title>: <content>"
void displayDocument(const Document& doc) {
// TODO: Implemente esta função
}
int main() {
// Ler entrada
string title;
string content;
getline(cin, title);
getline(cin, content);
// TODO: Crie um Document com os valores de entrada
// TODO: Imprima "Title: <title>"
// TODO: Imprima "Preview: <preview>"
// TODO: Print "Views after preview: <count>"
// TODO: Acesse o conteúdo completo e imprima "Content: <content>"
// TODO: Print "Views after first read: <count>"
// TODO: Acesse o conteúdo novamente e imprima "Content: <content>"
// TODO: Print "Views after second read: <count>"
// TODO: Chame displayDocument() com o seu documento
// TODO: Print "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 POO
Arquivos externosBuild e compilação em C++Arquivos de cabeçalho e arquivos-fonteNamespaces e escopoIntrodução à POO em C++Classes vs. objetosO ponteiro 'this'Métodos (funções membro)Atributos (membros de dados)Noções básicas de construtores e destruidoresRevisão - calculadora simples4Propriedades de Classe
Membros de Instância vs. EstáticosGetters e SettersFunções Membro ConstPalavra-chave MutableMétodos e Variáveis EstáticosFunções e Classes FriendRevisão - Gerenciador de Contas Bancárias7Herança
Herança básicaNíveis de acesso da herançaOrdem de chamada de construtor e destrutorSobrescrita de métodosFunções virtuais e VTableHerança múltiplaHerança virtualRevisão - Hierarquia de funcionários10Visão geral da STL
Visão geral e filosofia da STLContêineres da STLIteradoresAlgoritmos da STLFunctors e expressões lambdaRecapitulação - Frequência de palavras13Padrões de Projeto — Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonFactory e Abstract FactoryPadrão BuilderPadrão ObserverPadrão Strategy2Gerenciamento de Memória
Memória Stack vs. HeapPonteiros e ReferênciasMemória Dinâmica (new/delete)Ponteiros Inteligentes em C++RAII em C++Revisão - Gerenciador de Array Dinâmico5Encapsulamento
Especificadores de acesso em C++Especificadores de acesso em profundidadeOcultação de informaçõesStruct vs ClasseClasses aninhadas e internasRecapitulação - Sistema de registros de alunos8Polimorfismo
Polimorfismo em compilação vs. execuçãoSobrecarga de funçõesFunções virtuais revisadasFunções virtuais purasClasses abstratasDesign de interfaces em C++Conversão dinâmica e RTTIRecapitulação — Calculadora de formas11Conceitos Avançados de OOP
Composição vs. HerançaMixins via CRTPIdioma PimplApagamento de TiposClasses Enum e Tipagem ForteTratamento de Exceções em OOPHierarquias de Exceções Personalizadas14Padrões de Projeto — Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositeRAII como Padrão3Construtores e Destrutores
Construtor PadrãoConstrutor ParametrizadoConstrutor de CópiaConstrutor de MovimentoListas de Inicialização do ConstrutorConstrutores DelegantesAprofundamento em DestrutoresRegra dos Três / Cinco / ZeroRevisão - Classe String6Sobrecarga de Operadores
Introdução à Sobrecarga de OperadoresSobrecarga de Operadores AritméticosSobrecarga de Operadores de ComparaçãoOperadores de FluxoSobrecarga do Operador de AtribuiçãoSobrecarga dos Operadores [] e ()Operadores de Conversão de TipoRecapitulação - Classe Matrix9Templates
Templates de FunçõesTemplates de ClassesEspecialização de TemplatesTemplates VariádicosNoções Básicas de SFINAE e Traits de TiposRecapitulação - Contêiner GenéricoPratique por conta própria: Compilador de C++ online