Isso aborda principalmente a segunda linha: melhores práticas, atribuições, parâmetros de função etc.
Prática geral. Tente fazer tudo o constque puder. Ou, de outra forma, faça tudo constpara começar e remova exatamente o conjunto mínimo de consts 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 constobjeto, fará muito melhor para encontrar quem o declarou constno 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 funcagora 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á nullptrmenos algumas outras assegura garantia contratual funcque nunca vai receber um nullptrem 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.