Será que o 'mutável' palavra-chave ter qualquer outra finalidade de permitir que a variável a ser modificada por uma função const?

StackOverflow https://stackoverflow.com/questions/105014

  •  01-07-2019
  •  | 
  •  

Pergunta

Um tempo atrás eu vim através de um código que marcou uma variável de membro de uma classe com a palavra-chave mutable. Tanto quanto eu posso ver isso simplesmente permite que você modifique uma variável em um método const:

class Foo  
{  
private:  
    mutable bool done_;  
public:  
    void doSomething() const { ...; done_ = true; }  
};

Este é o único uso desta palavra-chave ou há mais a ele do que os olhos? Tenho visto que usou esta técnica em uma classe, marcando um boost::mutex como funções permitindo const mutáveis ??para travá-lo por razões thread-segurança, mas, para ser honesto, parece que um pouco de um hack.

Foi útil?

Solução

Ele permite a diferenciação de const bit a bit e const lógico. const lógica é quando um objeto não muda de uma forma que é visível através da interface pública, como o seu exemplo de bloqueio. Outro exemplo seria uma classe que calcula um valor da primeira vez que é solicitado, e armazena o resultado.

Desde c ++ 11 mutable pode ser usado em um lambda para denotar que as coisas capturados por valor são modificáveis ??(eles não são por padrão):

int x = 0;
auto f1 = [=]() mutable {x = 42;};  // OK
auto f2 = [=]()         {x = 42;};  // Error: a by-value capture cannot be modified in a non-mutable lambda

Outras dicas

A palavra-chave mutable é uma maneira de furar a const véu você drapejar sobre seus objetos. Se você tem uma referência const ou ponteiro para um objeto, você não pode modificar o objeto de qualquer maneira exceto quando e como ele é marcado mutable.

Com sua referência const ou ponteiro você está limitado a:

  • apenas o acesso de leitura para todos os membros de dados visíveis
  • permissão para chamar únicos métodos que são marcados como const.

A exceção mutable torna tão agora você pode escrever ou membros de dados conjunto que estão marcados mutable. Essa é a diferença apenas externamente visível.

Internamente esses métodos const que são visíveis para você também pode escrever para os membros de dados que são marcados mutable. Essencialmente o véu const é perfurado de forma abrangente. É completamente até o designer API para garantir que mutable não destrói o conceito const e só é usado em casos especiais úteis. A palavra-chave mutable ajuda porque claramente membros de dados marcas que estão sujeitas a estes casos especiais.

Na prática, você pode usar const obsessivamente em toda a sua base de código (você quer, essencialmente, a "contaminar" sua base de código com a "doença" const). Neste ponteiros mundo e referências são const com muito poucas excepções, produzindo código que é mais fácil de razão e compreender. Para uma digressão interessante olhar para cima "transparência referencial".

Sem o mutable palavra-chave que acabará por ser forçado a usar const_cast para lidar com os vários casos especiais úteis que permite (caching, contagem ref, dados de depuração, etc.). Infelizmente const_cast é significativamente mais destrutivo do que mutable porque força o API cliente para destruir a proteção const dos objetos (s) que ele está usando. Além disso, ele provoca a destruição const generalizada: const_casting um ponteiro const ou referência permite gravação irrestrito e método chamando o acesso aos membros visíveis. Em contraste mutable requer o designer API para o exercício de grão fino controle sobre as exceções const, e, geralmente, essas exceções estão escondidos em métodos const operando em dados privados.

(NB I referem-se a dados e método visibilidade algumas vezes. Estou falando de membros marcados como vs. pública privada ou protegidas que é um tipo totalmente diferente de proteção objeto discutido aqui .)

Seu uso com boost :: mutex é exatamente o que esta palavra-chave se destina. Outro uso é para o cache interno do resultado para o acesso velocidade.

Basicamente, 'mutável' se aplica a qualquer atributo de classe que não afeta o estado externamente visível do objeto.

No código de exemplo na sua pergunta, mutável pode não ser apropriado se o valor do done_ afeta estado externo, isso depende do que está no ...; parte.

Mutante é para a marcação de atributo específico como modificável de dentro métodos const. Esse é o seu único propósito. Pense bem antes de usá-lo, porque seu código será, provavelmente, mais limpo e mais legível se você mudar o desenho ao invés de uso mutable.

http://www.highprogrammer.com/alan/rants/mutable.html

Então, se a loucura acima não o que é mutável é a favor, o que é isso? aqui está o caso sutil: mutável é para o caso em que um objecto é logicamente constante, mas na prática deve mudança. Estes casos são poucos e distantes entre eles, mas eles existem.

Exemplos o autor dá incluem caching e variáveis ??de depuração temporários.

É útil em situações onde você tem escondido estado interno, como um cache. Por exemplo:

class HashTable
{
...
public:
    string lookup(string key) const
    {
        if(key == lastKey)
            return lastValue;

        string value = lookupInternal(key);

        lastKey = key;
        lastValue = value;

        return value;
    }

private:
    mutable string lastKey, lastValue;
};

E então você pode ter um objeto const HashTable ainda usam seu método lookup(), que modifica o cache interno.

Bem, sim, é isso que ele faz. Eu usá-lo para os membros que são modificados por métodos que não o fazem logicamente alterar o estado de uma classe - por exemplo, para acelerar as pesquisas através da implementação de um cache:

class CIniWrapper
{
public:
   CIniWrapper(LPCTSTR szIniFile);

   // non-const: logically modifies the state of the object
   void SetValue(LPCTSTR szName, LPCTSTR szValue);

   // const: does not logically change the object
   LPCTSTR GetValue(LPCTSTR szName, LPCTSTR szDefaultValue) const;

   // ...

private:
   // cache, avoids going to disk when a named value is retrieved multiple times
   // does not logically change the public interface, so declared mutable
   // so that it can be used by the const GetValue() method
   mutable std::map<string, string> m_mapNameToValue;
};

Agora, você deve usar isso com cuidado - problemas de concorrência são uma grande preocupação, como um chamador pode assumir que eles são thread-safe que apenas usando métodos const. E, claro, modificar dados mutable não deve alterar o comportamento do objeto de qualquer forma significativa, algo que poderia ser violados pelo exemplo que dei se, por exemplo, esperava-se que as mudanças gravados no disco seria imediatamente visível para o aplicativo.

mutable existe como você infere que permitem modificar dados em uma função de outra forma constante.

A intenção é que você pode ter uma função que para o estado interno do objeto "não faz nada", e assim que você marcar a função const, mas você pode realmente precisa modificar alguns do estado objetos de maneiras que don' t afetar a sua funcionalidade correta.

A palavra-chave pode agir como uma dica para o compilador - um compilador teórica poderia colocar um objeto constante (como um global) na memória que foi marcado somente leitura. A presença de dicas mutable que isso não deve ser feito.

Aqui estão algumas razões válidas para declarar e usar dados mutáveis:

  • Segmento de segurança. Declarando uma mutable boost::mutex é perfeitamente razoável.
  • Estatísticas. Contando o número de chamadas para uma função, dado algum ou todos os seus argumentos.
  • Memoização. Computação alguma resposta caro, e, em seguida, guardá-lo para referência futura, em vez de recalcular-lo novamente.

Mutante é usado quando você tem uma variável dentro da classe que é usada apenas dentro dessa classe para sinalizar coisas como por exemplo, um mutex ou um bloqueio. Esta variável não muda o comportamento da classe, mas é ordem na necessário implementar segurança de segmentos da própria classe. Assim, se não houver "mutável", você não seria capaz de ter funções "const", porque esta variável terá de ser alterado em todas as funções que estão disponíveis para o mundo exterior. Portanto, mutável foi introduzida a fim de fazer uma gravável variável de membro mesmo por uma função de const.

Os informa especificados mutáveis ??tanto o compilador e o leitor que é seguro e esperar que uma variável de membro pode ser modificado dentro de um const membro função.

mutável é usado principalmente em um detalhe de implementação da classe. O usuário da classe não precisa de saber sobre isso, portanto método é que ele acha que "deve" ser const pode ser. Seu exemplo de ter um mutex ser mutável é um bom exemplo canônico.

Seu uso dele não é um hack, mas como muitas coisas em C ++, mutável pode ser corte para um programador preguiçoso que não quer ir todo o caminho de volta e marca de algo que não deve ser const como não-const.

Use "mutável", quando para as coisas que são logicamente sem estado para o usuário (e, portanto, deve ter getters "Const" na classe pública APIs), mas não são apátridas na implementação subjacente (o código em seu CPP).

Os casos eu usá-lo com mais frequência são a inicialização lenta do estado-membros menos "planície dados antigos". Ou seja, é ideal nos casos estreitos quando tais membros são caros, quer de construção (processador) ou transportar em torno de (memória) e muitos usuários do objeto nunca vai pedir para eles. Nessa situação você quer construção preguiçoso no back-end para o desempenho, uma vez que 90% dos objetos construídos nunca vai precisar para construir-los em tudo, mas você ainda precisa apresentar a API apátrida correto para o consumo público.

Mutante muda o significado de const de const bit a bit para const lógica para a classe.

Isto significa que classes com membros mutáveis ??são mais ser const bit a bit e não mais aparecerão em seções somente leitura do executável.

Além disso, ele modifica de verificação de tipo, permitindo que funções de membro const para mudar membros mutáveis ??sem usar const_cast.

class Logical {
    mutable int var;

public:
    Logical(): var(0) {}
    void set(int x) const { var = x; }
};

class Bitwise {
    int var;

public:
    Bitwise(): var(0) {}
    void set(int x) const {
        const_cast<Bitwise*>(this)->var = x;
    }
};

const Logical logical; // Not put in read-only.
const Bitwise bitwise; // Likely put in read-only.

int main(void)
{
    logical.set(5); // Well defined.
    bitwise.set(5); // Undefined.
}

Veja as outras respostas para mais detalhes, mas eu queria destacar que não é apenas para o tipo-saftey e que isso afeta o resultado compilado.

Em alguns casos (como iteradores mal concebidos), as necessidades de classe para manter uma contagem ou algum outro valor incidental, que realmente não afeta o principal "estado" da classe. Este é o mais frequentemente onde eu vejo usado mutável. Sem mutável, você seria obrigado a sacrificar toda a const-ness do seu design.

Ela se sente como um hack a maior parte do tempo para mim também. Útil em muito poucas situações.

O exemplo clássico (como mencionado em outras respostas) ea única situação que eu vi a palavra-chave mutable usado em até agora, é para armazenar em cache o resultado de um método Get complicado, onde o cache é implementado como um membro de dados do classe e não como uma variável estática no método (por razões de partilha entre várias funções ou limpeza simples).

Em geral, as alternativas ao uso do mutable palavra-chave são geralmente uma variável estática no método ou o truque const_cast.

Outra explicação detalhada está em aqui .

O mutável pode ser útil quando você está substituindo uma função virtual const e deseja modificar sua variável de membro da classe criança nessa função. Na maioria dos casos, você não gostaria de alterar a interface da classe base, então você tem que usar variável de membro mutáveis ??de sua preferência.

A palavra-chave mutável é muito útil ao criar stubs para fins de teste de classe. Você pode stub uma função const e ainda ser capaz de aumentar contadores ou qualquer funcionalidade de teste que você adicionou ao seu esboço (mutáveis). Isso mantém a interface da classe apagou intacta.

Um dos melhores exemplos onde usamos é mutável, em cópia profunda. no construtor de cópia enviamos const &obj como argumento. Assim, o novo objeto criado será do tipo constante. Se queremos mudar (principalmente nós não vai mudar, no caso raro que pode mudar) os membros desse objeto const recém-criado é preciso declará-lo como mutable.

classe de armazenamento mutable pode ser usado somente em membro de dados não estático não const de uma classe. membro de dados mutáveis ??de uma classe pode ser modificado mesmo se parte, é de um objeto que é declarada como const.

class Test
{
public:
    Test(): x(1), y(1) {};
    mutable int x;
    int y;
};

int main()
{
    const Test object;
    object.x = 123;
    //object.y = 123;
    /* 
    * The above line if uncommented, will create compilation error.
    */   

    cout<< "X:"<< object.x << ", Y:" << object.y;
    return 0;
}

Output:-
X:123, Y:1

No exemplo acima, somos capazes de alterar o valor de x variável de membro que seja parte de um objeto que é declarada como const. Isso ocorre porque o x variável é declarada como mutável. Mas se você tentar modificar o valor de y variável de membro, compilador irá lançar um erro.

A própria palavra-chave 'mutável' é na verdade uma reservados keyword.often ele é usado para variar o valor da constante variable.If você quer ter vários valores de uma constsnt, use a palavra-chave mutável.

//Prototype 
class tag_name{
                :
                :
                mutable var_name;
                :
                :
               };   
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top