valor - passagem por referencia c++




Uso de 'const' para parâmetros de função (20)

Quão longe você vai com const ? Você apenas faz funções const quando necessário ou você vai todo o porco e usá-lo em todos os lugares? Por exemplo, imagine um simples modificador que usa um único parâmetro booleano:

void SetValue(const bool b) { my_val_ = b; }

Isso é realmente útil? Pessoalmente, opto por usá-lo extensivamente, incluindo parâmetros, mas neste caso me pergunto se vale a pena?

Também fiquei surpreso ao saber que você pode omitir const de parâmetros em uma declaração de função, mas pode incluí-la na definição da função, por exemplo:

arquivo .h

void func(int n, long l);

arquivo .cpp

void func(const int n, const long l)

Existe uma razão para isso? Parece um pouco incomum para mim.


Se você usar os operadores ->* ou .* , É uma obrigação.

Isso impede que você escreva algo como

void foo(Bar *p) { if (++p->*member > 0) { ... } }

o que eu quase fiz agora, e que provavelmente não faz o que você pretende.

O que eu pretendia dizer era

void foo(Bar *p) { if (++(p->*member) > 0) { ... } }

e se eu tivesse colocado um const entre Bar * e p , o compilador teria me dito isso.


Às vezes (muitas vezes!) Eu tenho que desvendar o código C ++ de outra pessoa. E todos nós sabemos que o código C ++ de outra pessoa é uma bagunça completa quase por definição :) Então a primeira coisa que faço para decifrar o fluxo de dados local é colocar const em todas as definições de variáveis ​​até que o compilador comece a latir. Isso significa que os argumentos de valor const-qualifying também, porque eles são apenas variáveis ​​locais fantasia inicializadas pelo chamador.

Ah, eu queria que as variáveis ​​fossem const por padrão e mutáveis fossem necessárias para variáveis ​​não-const :)


Ah, dificil. De um lado, uma declaração é um contrato e realmente não faz sentido passar um argumento const por valor. Por outro lado, se você observar a implementação da função, dará ao compilador mais chances de otimizar se declarar uma constante de argumento.


As duas linhas seguintes são funcionalmente equivalentes:

int foo (int a);
int foo (const int a);

Obviamente você não poderá modificar a no corpo do foo se for definido o segundo caminho, mas não há diferença do lado de fora.

Onde const realmente vem a calhar é com parâmetros de referência ou ponteiro:

int foo (const BigStruct &a);
int foo (const BigStruct *a);

O que isso diz é que foo pode ter um parâmetro grande, talvez uma estrutura de dados com gigabytes de tamanho, sem copiá-lo. Além disso, diz ao chamador: "O Foo não irá alterar o conteúdo desse parâmetro". Passar uma referência const também permite que o compilador tome certas decisões de desempenho.

*: A menos que elimine a constância, mas esse é outro post.


Eu costumo usar const sempre que possível. (Ou outra palavra-chave apropriada para o idioma de destino.) Faço isso simplesmente porque permite que o compilador faça otimizações extras que não seria possível fazer de outra forma. Como não tenho ideia de quais são essas otimizações, sempre faço isso, mesmo quando parece bobo.

Por tudo que eu sei, o compilador pode muito bem ver um parâmetro de valor const, e dizer: "Ei, essa função não está modificando de qualquer maneira, então eu posso passar por referência e salvar alguns ciclos de clock." Eu não acho que isso faria alguma coisa, uma vez que muda a assinatura da função, mas faz o ponto. Talvez ele faça alguma manipulação de pilha diferente ou algo assim ... O ponto é, eu não sei, mas eu sei que tentar ser mais esperto que o compilador só me leva a ser envergonhado.

O C ++ tem alguma bagagem extra, com a ideia de const-correctness, por isso torna-se ainda mais importante.


Eu digo const seus parâmetros de valor.

Considere esta função de buggy:

bool isZero(int number)
{
  if (number = 0)  // whoops, should be number == 0
    return true;
  else
    return false;
}

Se o parâmetro numérico fosse const, o compilador pararia e nos avisaria do erro.


Eu uso const em parâmetros de função que são referências (ou ponteiros) que são apenas [no] dados e não serão modificados pela função. Ou seja, quando o propósito de usar uma referência é evitar copiar dados e não permitir a alteração do parâmetro passado.

Colocar const no parâmetro boolean b em seu exemplo coloca apenas uma restrição na implementação e não contribui para a interface da classe (embora não seja recomendável alterar os parâmetros).

A assinatura da função para

void foo(int a);

e

void foo(const int a);

é o mesmo, o que explica seu .c e .h

Asaf


Eu uso const quando eu posso. Const para parâmetros significa que eles não devem alterar seu valor. Isso é especialmente valioso ao passar por referência. const for function declara que a função não deve mudar os membros das classes.


Há uma boa discussão sobre este tópico no antigo artigo "Guru da Semana" em comp.lang.c ++. Moderado here .

O artigo GOTW correspondente está disponível no site da Herb Sutter here .


No caso de você mencionar, isso não afeta os chamadores de sua API, e é por isso que não é comumente feito (e não é necessário no cabeçalho). Isso afeta apenas a implementação da sua função.

Não é algo particularmente ruim, mas os benefícios não são tão bons, já que isso não afeta sua API e adiciona digitação, portanto, isso geralmente não é feito.


Para resumir:

  • "Normalmente, const pass by value é inútil e enganoso na melhor das hipóteses." De here
  • Mas você pode adicioná-los no .cpp como faria com as variáveis.
  • Note que a biblioteca padrão não usa const. Por exemplo, std::vector::at(size_type pos) . O que é bom o suficiente para a biblioteca padrão é bom para mim.

Pode ser que isso não seja um argumento válido. mas se incrementarmos o valor de uma variável const dentro de um compilador de função nos dará um erro: " error: incremento do parâmetro somente leitura ". então isso significa que podemos usar a palavra-chave const como uma maneira de evitar modificar acidentalmente nossas variáveis ​​dentro das funções (que não devemos / somente leitura). então se nós acidentalmente fizermos isso no compilador, o compilador nos informará. isso é especialmente importante se você não for o único que está trabalhando neste projeto.


Se o parâmetro é passado por valor (e não é uma referência), geralmente não há muita diferença se o parâmetro é declarado como const ou não (a menos que contenha um membro de referência - não é um problema para tipos internos). Se o parâmetro é uma referência ou ponteiro, geralmente é melhor proteger a memória referenciada / apontada, não o próprio ponteiro (eu acho que você não pode fazer a própria referência const, não que isso importe muito, pois você não pode mudar o árbitro) . Parece uma boa ideia proteger tudo o que puder como const. Você pode omiti-lo sem medo de cometer um erro se os parâmetros forem apenas PODs (incluindo tipos incorporados) e não houver nenhuma chance deles mudarem ao longo do caminho (por exemplo, no seu exemplo o parâmetro bool).

Eu não sabia sobre a diferença de declaração do arquivo .h / .cpp, mas faz algum sentido. No nível de código de máquina, nada é "const", portanto, se você declarar uma função (no .h) como não-const, o código é o mesmo que se você declarar como const (otimizações de lado). No entanto, ele ajuda você a inscrever o compilador que você não irá alterar o valor da variável dentro da implementação da função (.ccp). Pode ser útil quando você está herdando de uma interface que permite a alteração, mas não é necessário alterar o parâmetro para obter a funcionalidade necessária.


const é inútil quando o argumento é passado por valor, pois você não modifica o objeto do chamador.

const deve ser preferido ao passar por referência, a menos que o propósito da função seja modificar o valor passado.

Finalmente, uma função que não modifica o objeto atual (this) pode, e provavelmente deve ser declarada const. Um exemplo está abaixo:

int SomeClass::GetValue() const {return m_internalValue;}

Esta é uma promessa de não modificar o objeto ao qual esta chamada é aplicada. Em outras palavras, você pode ligar:

const SomeClass* pSomeClass;
pSomeClass->GetValue();

Se a função não fosse const, isso resultaria em um aviso do compilador.


"const é inútil quando o argumento é passado por valor desde que você não estará modificando o objeto do chamador."

Errado.

É sobre auto-documentar seu código e suas suposições.

Se o seu código tem muitas pessoas trabalhando nele e suas funções não são triviais, então você deve marcar "const" qualquer e tudo que você puder. Ao escrever um código de força industrial, você deve sempre presumir que seus colegas de trabalho são psicopatas que tentam fazer com que você consiga tudo o que puderem (especialmente porque muitas vezes é você mesmo no futuro).

Além disso, como alguém mencionou anteriormente, isso pode ajudar o compilador a otimizar as coisas um pouco (embora seja um tiro longo).


Como os parâmetros estão sendo passados ​​por valor, não faz qualquer diferença se você especificar const ou não a partir da perspectiva da função de chamada. Basicamente, não faz sentido declarar parâmetros de passagem por valor como const.


Não há realmente nenhum motivo para criar um parâmetro de valor "const", pois a função só pode modificar uma cópia da variável de qualquer maneira.

A razão para usar "const" é se você está passando algo maior (por exemplo, uma struct com muitos membros) por referência, e nesse caso ele garante que a função não possa modificá-lo; ou melhor, o compilador irá reclamar se você tentar modificá-lo da maneira convencional. Evita que seja acidentalmente modificado.


Sendo um programador VB.NET que precisa usar um programa C ++ com mais de 50 funções expostas e um arquivo .h que usa esporadicamente o qualificador const, é difícil saber quando acessar uma variável usando ByRef ou ByVal.

É claro que o programa informa, gerando um erro de exceção na linha onde você cometeu o erro, mas então você precisa adivinhar qual dos 2-10 parâmetros está errado.

Então agora eu tenho a tarefa desagradável de tentar convencer um desenvolvedor de que eles devem realmente definir suas variáveis ​​(no arquivo .h) de uma maneira que permita um método automatizado de criar facilmente todas as definições de função do VB.NET. Eles então dirão, presunçosamente, "leia a ... documentação".

Eu escrevi um script awk que analisa um arquivo .h, e cria todos os comandos Declare Function, mas sem um indicador de quais variáveis ​​são R / O versus R / W, ele faz metade do trabalho.

EDITAR:

Com o incentivo de outro usuário, estou adicionando o seguinte;

Aqui está um exemplo de uma entrada .h malformada (IMO);

typedef int (EE_STDCALL *Do_SomethingPtr)( int smfID, const char* cursor_name, const char* sql );

O VB resultante do meu script;

    Declare Function Do_Something Lib "SomeOther.DLL" (ByRef smfID As Integer, ByVal cursor_name As String, ByVal sql As String) As Integer

Observe a falta "const" no primeiro parâmetro. Sem ele, um programa (ou outro desenvolvedor) não tem idéia do primeiro parâmetro deve ser passado "ByVal". Ao adicionar o "const", o arquivo .h é documentado de forma que os desenvolvedores que usam outras linguagens podem facilmente escrever código de trabalho.


Const parâmetro é útil somente quando o parâmetro é passado por referência ou seja, referência ou ponteiro. Quando o compilador vê um parâmetro const, certifique-se de que a variável usada no parâmetro não seja modificada dentro do corpo da função. Por que alguém iria querer fazer um parâmetro por valor como constante? :-)


Eu não colocaria parâmetros constantes como esse - todo mundo já sabe que um booleano (em oposição a um booleano) é constante, então adicioná-lo fará as pessoas pensarem "espere, o quê?" ou até mesmo que você esteja passando o parâmetro por referência.





const