Todos os objetos no C ++ são mutáveis, se não indicado de outra forma?

8

Todo objeto no C ++ é mutável se não indicado de outra forma?

Em Python e Javascript, não posso alterar strings, tupla, unicodes. Eu queria saber se existe algo em C ++ que é imutável ou que todo objeto é mutável e eu tenho que usar o constqualificador de tipo para torná-lo imutável.

GM
fonte
2
o que você quer dizer com "por padrão"?
BЈовић
Que todos os objetos em C ++ são mutáveis ​​se não declarados de maneira diferente.
GM
1
Sim, eles estão. Observe que isso não é o mesmo que a palavra-chave "mutável".
Olav
3
A pergunta no corpo da pergunta é diferente da pergunta no título?
precisa saber é o seguinte
@immibis foi apenas para adicionar um pouco pequena hospedaria de concurso
GM

Respostas:

18

A imutabilidade é bem compreendida há algum tempo. Python, Java e C ++ têm diferentes modelos de memória que dificultam as comparações diretas. O autor do artigo que você citou originalmente não parece conhecer C ++.

Como em Python, Java e na maioria das linguagens de múltiplos paradigmas, C e C ++ permitem mutabilidade por padrão. Isto é o que os programadores geralmente querem: se eu tiver uma String xvariável, desejo poder atribuir um novo valor x = "foo".

O sistema const em C e C ++ permite uma grande imutabilidade sutil que falta em Python, Java e até Scala. Se uma função C ++ usa a const std::string&ou a const char*, promete (e o compilador garante, até certo ponto), que essa função não irá (não pode!) Alterar o conteúdo dessa string. Dado um objeto const, só podemos invocar métodos desse objeto que também são marcados como const. Se uma classe C ++ tiver apenas membros públicos const, o objeto será efetivamente imutável.

No entanto, isso às vezes é confuso, pois os objetos C e C ++ são locais de memória e variáveis ​​são nomes para locais de memória. Por outro lado, variáveis ​​em Python e Java são nomes para ponteiros para objetos. Em uma linguagem com semântica de referência, x = ysignifica "faça x apontar para o mesmo objeto que y". Como estamos apenas copiando ponteiros, isso é possível com objetos imutáveis. Em uma linguagem com semântica de valores como C ++, significa "atualizar o conteúdo de xcom o conteúdo de y". Portanto, se a redistribuição de uma variável for desejada em C ou C ++, a variável pode não ter um tipo const. Para fazer isso com objetos imutáveis, teríamos que usar um ponteiro explicitamente.

O fato de Java e Python usarem objetos de sequência imutáveis ​​é uma decisão de design fundamental, mas não está diretamente conectada aos benefícios da imutabilidade em um ambiente de multithreading. Uma razão é que literais de string no código-fonte podem ser agrupados, o que reduz o número de objetos. Isso também é possível em C / C ++. Em C ++, o literal "foo"tem tipo const char[4](o quarto caractere é o final '\0'). Outro motivo é que as entradas em conjuntos e chaves em dictos / mapas não devem ser alteradas. Como as seqüências de caracteres são usadas amplamente como chaves de ditado (a maioria dos objetos Python é um ditado), a imutabilidade remove uma fonte comum de erros. Em Java, outro motivo para sequências imutáveis ​​é o modelo de segurança Java. Todos esses motivos não têm nenhuma relação com o multithreading.

Se o Java fosse construído com imutabilidade em mente, a linguagem teria uma aparência muito diferente. Embora seja intimamente inspirado pelo C ++, os designers tentaram criar uma linguagem muito mais simples, livrar-se do const é um desses passos. A coisa Java equivalente a uma referência const C ++ é um adaptador ou decorador que implementa qualquer método de mutação como throws new NotImplementedException()e encaminha chamadas de método não mutante para a coleção real. O fato de a coleção java.util interagir com todos implica em mutabilidade é um sinal claro de que eles não se esforçaram por um idioma de imutabilidade em primeiro lugar.

A solução que o Java apresentou para solucionar problemas de concorrência não era imutabilidade, mas um bloqueio generalizado. Cada objeto contém um mutex que pode ser usado para synchronizedblocos ou métodos inteiros. Como se vê, isso não é bom para o desempenho, não se adapta muito bem e é propenso a erros - você ainda precisa lidar com um estado global mutável.

amon
fonte
13
Isso é o que os programadores geralmente querem => essa é a afirmação controversa. Minha experiência é realmente o oposto: a maioria das variáveis ​​locais é imutável, com a ocasionalmente mutável. Idiomas como Rust (onde a mutabilidade requer uma mutpalavra - chave e onde a mutabilidade desnecessária é sinalizada pelo compilador) tendem a apoiar minha hipótese: no Rust, você tem muito mais letligações do que as que possui let mut.
Matthieu M.
1
@ MatthieuM .: Isso é parcialmente porque for x in 1..10não é usado let mutpara definir a variável do loop, mas obviamente requer alguma mutabilidade (apenas no nível do loop, não dentro do corpo do loop).
precisa saber é o seguinte
@MSalters: Claro, mas C ++ tem uma gama-de sintaxe também :)
Matthieu M.
4
@MSalters eu caracterizaria isso como ligação 10 variáveis chamados x com 10 valores, não de mutação
Caleth
1
@MatthieuM. Concordo que a imutabilidade é legal, mas em C ++ nem sempre é uma opção, ou pelo menos excessivamente desajeitada. Por exemplo, inicializar uma variável const em várias etapas requer que façamos isso em uma função separada, onde esse objeto ainda não é const. Pelo menos C ++ 11, vamos usar lambdas imediatamente invocadas como const T o = [&]{T o; o.init(...); return o;}();... sim, para APIs que não foram projetadas com imutabilidade em mente.
amon
9

Quase. A própria linguagem trata tudo como mutável, a menos que você use const, mas ainda é possível criar objetos imutáveis ​​sem dizer ao compilador que são imutáveis, simplesmente não fornecendo uma maneira de mutá-los.

Por exemplo, pegue esta classe:

class MyClass {
    int value;
public:

    MyClass(int v) : value(v) {}
    int getValue() {return value;}

    MyClass &operator=(const MyClass &other) = delete;
};

Instâncias de MyClasssão imutáveis, porque não há como modificá-las - mas em nenhum lugar do código se afirma que elas são imutáveis. (É da mesma maneira que instâncias da Stringclasse Java são imutáveis)

user253751
fonte
(Estou ficando enferrujado) O deleteoperador de atribuição de cópia aqui também garante que não seja possível atribuir a partir de uma referência de valor através da operação de atribuição de movimento?
underscore_d
E destruir o objeto com uma chamada de destruidor explícita e depois recriá-lo com a colocação de nova contagem como "mutante"?
Peter Green
1
@underscore_d Ele conta como o operador para efeitos de não acrescentar implicitamente uma atribuição movimento, sim, definir usuário
Caleth
Gostaria de saber se um mecanismo para permitir que o compilador saiba que objetos de uma determinada classe são sempre imutáveis ​​poderia levar a uma melhor otimização. Por exemplo, seria desnecessário atualizar objetos descontados. Neste exemplo em particular (e geralmente nas classes que seguem esse padrão), provavelmente ajudaria a declarar valueconst e a declarar getValue()uma função const.
Peter - Restabelece Monica
1
@ PeterGreen Não, isso conta como destruir o objeto e criar um novo.
precisa saber é o seguinte
2

Não, há uma exceção: Lambdas e por extensão os objetos que capturam são imutáveis ​​por padrão:

int i = 0;

[i]{//capturing the value i
    i++; //does not compile, i is const
};

[i]() mutable{ //make the lambda mutable
    i++; //compiles fine
};

Então, em vez de adicionar constpara torná-lo imutável, você adiciona mutablepara torná-lo não const.

nwp
fonte