Isso aborda principalmente a segunda linha: melhores práticas, atribuições, parâmetros de função etc.
Prática geral. Tente fazer tudo o const
que puder. Ou, de outra forma, faça tudo const
para começar e remova exatamente o conjunto mínimo de const
s necessário para permitir que o programa funcione. Isso será uma grande ajuda para alcançar a correção constante e ajudará a garantir que erros sutis não sejam introduzidos quando as pessoas tentam atribuir coisas que não deveriam modificar.
Evite const_cast <> como uma praga. Existem um ou dois casos de uso legítimos para isso, mas eles são muito poucos e distantes entre si. Se você estiver tentando mudar um const
objeto, fará muito melhor para encontrar quem o declarou const
no primeiro ritmo e conversar sobre o assunto com eles para chegar a um consenso sobre o que deve acontecer.
O que leva muito bem às tarefas. Você pode atribuir a algo apenas se não for const. Se você deseja atribuir algo que seja const, veja acima. Lembre-se de que nas declarações int const *foo;
e nas int * const bar;
coisas diferentes existem const
- outras respostas aqui abordaram esse assunto de maneira admirável, por isso não vou entrar nisso.
Parâmetros da função:
Passe por valor: por exemplo, void func(int param)
você não se importa de uma maneira ou de outra no local da chamada. Pode-se argumentar que há casos de uso para declarar a função como void func(int const param)
mas que não têm efeito no chamador, apenas na própria função, na medida em que qualquer valor passado não pode ser alterado pela função durante a chamada.
Passe por referência: por exemplo, void func(int ¶m)
agora faz a diferença. Conforme declarado, func
é permitido mudar param
, e qualquer site de chamadas deve estar pronto para lidar com as consequências. Mudar a declaração para void func(int const ¶m)
mudar o contrato e garantir que func
agora não pode mudar param
, significando que o que é passado é o que será devolvido. Como outros observaram, isso é muito útil para a passagem barata de um objeto grande que você não deseja alterar. Passar uma referência é muito mais barato do que passar um objeto grande por valor.
Passe pelo ponteiro: por exemplo, void func(int *param)
e void func(int const *param)
Estes dois são praticamente sinônimo com os seus homólogos de referência, com a ressalva de que a função chamada agora precisa verificar se há nullptr
menos algumas outras assegura garantia contratual func
que nunca vai receber um nullptr
em param
.
Opinião sobre esse tópico. Provar a correção em um caso como esse é terrivelmente difícil, é muito fácil cometer um erro. Portanto, não se arrisque e sempre verifique os parâmetros do ponteiro nullptr
. Você economizará dor e sofrimento e será difícil encontrar insetos a longo prazo. E quanto ao custo da verificação, é muito barato e, nos casos em que a análise estática incorporada no compilador pode gerenciá-la, o otimizador o elimina de qualquer maneira. Ative a geração de código de tempo de link para MSVC ou WOPR (eu acho) para o GCC, e você obterá todo o programa, ou seja, mesmo em chamadas de função que cruzam o limite de um módulo de código-fonte.
No final do dia, tudo o que foi dito acima é um argumento muito sólido para sempre preferir referências a ponteiros. Eles são apenas mais seguros o tempo todo.