Vender-me na const correção

133

Então, por que exatamente é sempre recomendável usar const o mais rápido possível? Parece-me que usar const pode ser mais uma dor do que uma ajuda em C ++. Mas, novamente, estou abordando isso da perspectiva do python: se você não quiser que algo seja alterado, não mude. Então, com isso dito, aqui estão algumas perguntas:

  1. Parece que toda vez que eu marcar algo como const, recebo um erro e tenho que alterar alguma outra função em algum lugar para ser const também. Então isso me faz ter que mudar outra função em outro lugar. Isso é algo que fica mais fácil com a experiência?

  2. Os benefícios do uso de const realmente suficientes para compensar o problema? Se você não pretende alterar um objeto, por que não simplesmente não escrever um código que não o mude?

Devo observar que, neste momento, estou mais focado nos benefícios do uso de const para fins de correção e manutenção, embora também seja bom ter uma idéia das implicações de desempenho.

Jason Baker
fonte
1
Retrospectiva antiga de 8 anos, mas ... Que tal acelerar o código 100x (visto ao vivo)?
lorro 29/07
1
Verifique a minha resposta aqui (questão relacionada, eu diria mesma resposta): stackoverflow.com/questions/42310477/... BTW: eu faço amorconst
Gines Hidalgo

Respostas:

158

Este é o artigo definitivo sobre "const correctness": https://isocpp.org/wiki/faq/const-correctness .

Em poucas palavras, usar const é uma boa prática porque ...

  1. Ele protege você de alterar acidentalmente variáveis ​​que não se destinam a serem alteradas,
  2. Ele protege você de fazer atribuições acidentais de variáveis ​​e
  3. O compilador pode otimizá-lo. Por exemplo, você está protegido contra

    if( x = y ) // whoops, meant if( x == y )

Ao mesmo tempo, o compilador pode gerar código mais eficiente porque sabe exatamente qual será o estado da variável / função em todos os momentos. Se você estiver escrevendo um código C ++ rígido, isso é bom.

Você está certo, pois pode ser difícil usar a correção constante de forma consistente, mas o código final é mais conciso e seguro para programar. Quando você faz muito desenvolvimento em C ++, os benefícios disso se manifestam rapidamente.

Jordan Parmer
fonte
Sempre presumi que os valores const pudessem ser armazenados em cache livremente e, assim, evitar muitos problemas de simultaneidade ou melhorar a velocidade. Isso é verdade?
Phil H
3
Certo. constAs variáveis podem melhorar a velocidade, no sentido de que o compilador pode gerar um código mais ideal, pois conhece toda a intenção e uso da variável. Você notará a diferença? Bem, isso é discutível em sua inscrição. No que diz respeito à simultaneidade, as variáveis ​​constantes são somente leitura, o que significa que não há necessidade de bloqueios exclusivos nessas variáveis, pois o valor sempre será o mesmo.
Jordan Parmer
4
A partir do C ++ 11, a biblioteca padrão também supõe constsegurança para threads e o tratamento do seu próprio código dessa maneira facilita a verificação de possíveis problemas de multiencadeamento e é uma maneira fácil de fornecer garantias de API para seus usuários.
quer
3
Para mim, uma das maiores agregações de valor da correção const é que você sabe apenas observando os protótipos de funções quando ponteiros fazem referência a dados que nunca são alterados pela função (por exemplo, apenas entrada).
cp.engr 27/01
1
Não se esqueça que existe uma constância lógica e física . Qualquer objeto agregado contido que esteja marcado como mutável pode mudar, embora marcá-lo como tal implica que ele não tem nada a ver com a constância lógica do objeto que o contém.
Adrian
128

Aqui está um pedaço de código com um erro comum contra o qual a correção constante pode protegê-lo:

void foo(const int DEFCON)
{
   if (DEFCON = 1)     //< FLAGGED AS COMPILER ERROR! WORLD SAVED!
   {
       fire_missiles();
   }
}
Doug T.
fonte
19
Então, estamos dizendo que const é necessário porque algum idiota sangrento escolheu "=" como o operador de atribuição em C? ;-)
Steve Jessop
37
Obviamente, a maioria dos compiladores modernos avisa sobre atribuições em condicionais e todos ativamos os avisos de tratamento como sinalizador de erros, certo: ->
Jack Bolding
7
Não seja muito duro com esse exemplo: este é o mesmo exemplo evangelizado por alguns desenvolvedores para nos convencer a usar if (0 == i) em vez de if (i == 0). Por fim, o padrão de código de Doug pode ser usado fora da instrução "if". O importante é que mostra um dos benefícios do const de maneira bem-humorada. + 1.
paercebal
2
Alguns compiladores (e fiapos) alertam sobre isso há muito tempo, e isso é muito mais útil para capturar esse erro de digitação do que const: codepad.org/Nnf5JUXV .
7
@ Roger, você presume que vivemos em um mundo em que as construções são limpas e sem aviso prévio. Minha experiência é que, em muitos códigos, os avisos se perdem no barulho. Além disso, existe muito código que faz atribuições na expressão if e muitos argumentam que é um estilo "bom" válido.
Doug T.
62

Parece que toda vez que eu marcar algo como const, recebo um erro e tenho que alterar alguma outra função em algum lugar para ser const também. Então isso me leva a ter que mudar outra função em outro lugar. Isso é algo que fica mais fácil com a experiência?

Por experiência, este é um mito total. Isso acontece quando non-const-correct se assenta com o código const-correct, com certeza. Se você projeta a correção constante desde o início, isso NUNCA deve ser um problema. Se você criar algo const e depois algo não se compilar, o compilador está dizendo algo extremamente importante e você deve reservar um tempo para corrigi-lo corretamente .

Chris Mayer
fonte
7
Isso me ajudou a evitar tantos bugs nos quais eu tinha uma função const prestes a chamar uma função não-const porque esqueci que ele modifica os dados abaixo.
quer
9
Pouquíssimas pessoas sempre começam com bases de código limpas. A maioria das codificações é a manutenção do código legado, onde o comentário citado é bastante preciso. Uso const ao escrever novas funções de folha, mas questiono se vale a pena procurar coisas através de meia dúzia ou uma dúzia de níveis de chamada de código desconhecido.
Warren Dew
3
Isso está completamente errado na minha experiência. Os tipos de pontos aninhados criam a maior dor de cabeça para esse tipo de coisa, onde você pode ter vários quantificadores const em um único tipo.
Noldorin 23/05
4
"Se você cria uma correção constante desde o início", quantas vezes você realmente tem o luxo de forçar a correção correta desde o início? Muitos de nós temos que lidar com inúmeras APIs e bibliotecas diariamente, onde esse não é o caso e há pouco que você pode fazer sobre isso. Então, eu concordo com o sentimento do OP "Parece que toda vez que eu marcar algo como const, recebo um erro" "...
nyholku 28/03/19
@WarrenDew Esse é um argumento transitivo; se os autores originais tivessem usado constcorretamente (isto é, "desde o início"), então de fato não haveria um problema, que é exatamente o ponto!
Corridas da Luz em Órbita
31

Não é para você quando você está escrevendo o código inicialmente. É para alguém (ou você, alguns meses depois) que está olhando a declaração do método dentro da classe ou interface para ver o que ela faz. Não modificar um objeto é uma informação significativa a ser obtida.

Antonio Haley
fonte
1
Isso é meio que verdade. Const também é usado como proteção para impor variáveis ​​dentro de interfaces e assim por diante.
Jordan Parmer
É, mas você pode aplicá-lo por meio de práticas de codificação disciplinadas, e os estados dos pôsteres. O benefício real vem do fato de isso estar refletido na API.
Antonio Haley
2
Exatamente. É melhor ter "const int Value = 5;" que possuem "int ConstValue = 5;". +1.
22468 paercebal
6
O momento certo para colocar a correção constante é quando você escreve a API inicialmente. Caso contrário, você terá problemas com envenenamento por const quando o adaptar (é por isso que adicioná-lo ao código antigo é uma questão completamente diferente de fazê-lo no novo código).
Donal Fellows
@DonalFellows isso não está dizendo colocar const mais tarde. Ele está dizendo que você obter os benefícios mais tarde, quando você ler o código com const já presente
Caleth
27

Se você usa const rigorosamente, ficaria surpreso com a quantidade de variáveis ​​reais na maioria das funções. Muitas vezes, não mais do que um contador de loop. Se o seu código está chegando a esse ponto, você tem uma sensação calorosa por dentro ... validação por compilação ... o domínio da programação funcional está próximo ... você quase pode tocá-lo agora ...

QBziZ
fonte
1
desde C ++ 17 e a bondade constexpr Estou escrevendo testes de unidade de tempo de compilação ... chegando mais perto ainda ...
QBziZ
22

const é uma promessa que você está fazendo como desenvolvedor e solicita a ajuda do compilador para fazer cumprir.

Minhas razões para estar constantemente correto:

  • Ele comunica aos clientes de sua função que você não alterará a variável ou o objeto
  • Aceitar argumentos por referência const fornece a eficiência de passar por referência com a segurança de passar por valor.
  • Escrever suas interfaces como const correct permitirá que os clientes as usem. Se você escrever sua interface para receber referências que não sejam const, os clientes que estiverem usando const precisarão expulsar constness para trabalhar com você. Isso é especialmente irritante se sua interface aceita caracteres não-const * e seus clientes estão usando std :: strings, pois você só pode obter um const * deles.
  • O uso de const recrutará o compilador para mantê-lo honesto, para que você não altere por engano algo que não deve mudar.
JohnMcG
fonte
20

Minha filosofia é que, se você usar uma linguagem exigente com verificações de tempo de compilação, faça o melhor uso possível. consté uma maneira imposta pelo compilador de comunicar o que você quer dizer ... é melhor do que comentários ou doxygen jamais serão. Você está pagando o preço, por que não deduzir o valor?

Pat Notz
fonte
20

Programar C ++ sem const é como dirigir sem o cinto de segurança.

É doloroso colocar o cinto de segurança cada vez que você entra no carro e 364 de 365 dias você chega em segurança.

A única diferença é que, quando você tiver problemas com seu carro, sentirá isso imediatamente, enquanto que com a programação sem const, talvez seja necessário procurar por duas semanas o que causou esse acidente, apenas para descobrir que você inadvertidamente confundiu um argumento de função que você passou por referência não const para obter eficiência.

andreas buykx
fonte
18

Para programação incorporada, usando const criterioso ao declarar estruturas de dados globais pode economizar muita memória RAM, fazendo com que os dados constantes sejam localizados na ROM ou no flash sem copiar para a RAM no momento da inicialização.

Na programação diária, usar const cuidadoso ajuda a evitar a gravação de programas que travam ou se comportam de maneira imprevisível, porque eles tentam modificar literais de seqüência de caracteres e outros dados globais constantes.

Ao trabalhar com outros programadores em projetos grandes, o uso constadequado ajuda a impedir que outros programadores o estrangulem.

bk1e
fonte
13

constajuda a isolar o código que "muda as coisas" pelas suas costas. Portanto, em uma classe, você marcaria todos os métodos que não alteram o estado do objeto como const. Isso significa que constinstâncias dessa classe não poderão mais chamar nenhum não constmétodo. Dessa forma, você é impedido de chamar acidentalmente funcionalidades que podem alterar seu objeto.

Além disso, constfaz parte do mecanismo de sobrecarga, portanto, você pode ter dois métodos com assinaturas idênticas, mas um com conste um sem. Aquele com consté chamado para constreferências e o outro é chamado para não- constreferências.

Exemplo:

#include <iostream>

class HelloWorld {
    bool hw_called;

public:
    HelloWorld() : hw_called(false) {}

    void hw() const {
        std::cout << "Hello, world! (const)\n";
        // hw_called = true;  <-- not allowed
    }

    void hw() {
        std::cout << "Hello, world! (non-const)\n";
        hw_called = true;
    }
};

int
main()
{
    HelloWorld hw;
    HelloWorld* phw1(&hw);
    HelloWorld const* phw2(&hw);

    hw.hw();    // calls non-const version
    phw1->hw(); // calls non-const version
    phw2->hw(); // calls const version
    return 0;
}
Chris Jester-Young
fonte
13

A correção constante é uma daquelas coisas que realmente precisam estar em vigor desde o início. Como você descobriu, é muito difícil incluí-lo mais tarde, especialmente quando há muita dependência entre as novas funções que você está adicionando e as funções antigas que não estão corretas, que já existem.

Em grande parte do código que escrevo, realmente valeu a pena o esforço, porque costumamos usar muito a composição:

class A { ... }
class B { A m_a; const A& getA() const { return m_a; } };

Se não tivéssemos correção constante, você teria que recorrer a objetos complexos por valor para garantir a si mesmo que ninguém estava manipulando o estado interno da classe B pelas suas costas.

Em resumo, a correção constante é um mecanismo de programação defensivo para se livrar da dor no caminho.

Peter Kovacs
fonte
7

Digamos que você tenha uma variável em Python. Você sabe que não deveria modificá-lo. E se você acidentalmente fizer?

O C ++ oferece uma maneira de se proteger acidentalmente de fazer algo que você não deveria poder fazer em primeiro lugar. Tecnicamente, você pode contorná-lo de qualquer maneira, mas precisa fazer um trabalho extra para se matar.

Greg Rogers
fonte
4

Há um bom artigo aqui sobre const em c ++. É uma opinião bastante direta, mas espero que ajude alguns.

Ólafur Waage
fonte
3

Quando você usa a palavra-chave "const", está especificando outra interface para suas classes. Existe uma interface que inclui todos os métodos e uma interface que inclui apenas os métodos const. Obviamente, isso permite restringir o acesso a algumas coisas que você não deseja que sejam alteradas.

Sim, fica mais fácil com o tempo.

Wesley Tarle
fonte
2

Eu gosto de const constance ... em teoria. Sempre que tentei aplicá-lo rigorosamente na prática, ele acabou se deteriorando e o const_cast começa a se tornar cada vez mais feio.

Talvez sejam apenas os padrões de design que eu uso, mas o const sempre acaba sendo um pincel muito amplo.

Por exemplo, imagine um mecanismo de banco de dados simples ... ele possui objetos de esquema, tabelas, campos etc. os dados associados à tabela? Se o método Insert () estiver marcado como const, internamente, ele deverá eliminar a constância para realmente manipular o banco de dados. Se não estiver marcado const, não será protegido contra a chamada do método AddField.

Talvez a resposta seja dividir a classe com base nos requisitos de constância, mas isso tende a complicar o design mais do que eu gostaria pelo benefício que ele traz.

Rob Walker
fonte
Eu acho que seu exemplo é um caso de uso excessivo de const, mas não se esqueça do modificador mutável, que pode ser aplicado a variáveis ​​de instância para remover a constância.
Zooba 26/09/08
2
Quando uma função Insert () seria marcada como const, a menos que seja nomeada incorretamente? A adição de itens geralmente modifica o item ao qual você o adicionou, o que significa que não é const. O que você realmente queria era um TableWithConstSchema.
Greg Rogers
Eu poderia adicionar outra classe, mas não quero tornar a API desnecessariamente complexa apenas por uma questão de correção constante.
Rob Walker
1

Você pode dar dicas ao compilador com const também .... conforme o código a seguir

#include <string>

void f(const std::string& s)
{

}
void x( std::string& x)
{
}
void main()
{
    f("blah");
    x("blah");   // won't compile...
}
Keith Nicholas
fonte