Pergunta

Até onde você ir com const? Você apenas fazer funções const quando necessário ou você ir o porco inteiro e usá-lo em todos os lugares? Por exemplo, imagine um modificador simples que leva um único parâmetro boolean:

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

É que const realmente útil? Pessoalmente, eu optar por usá-lo extensivamente, incluindo parâmetros, mas neste caso eu me pergunto se vale a pena?

Eu também fiquei surpreso ao saber que você pode omitir const a partir de parâmetros em uma declaração de função, mas pode incluí-lo 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 estranho para mim.

Foi útil?

Solução

A razão é que const para o parâmetro aplica-se apenas localmente dentro da função, uma vez que está trabalhando em uma cópia dos dados. Isto significa que a assinatura da função é realmente a mesma de qualquer maneira. É provavelmente mau estilo de fazer isso muito embora.

Eu, pessoalmente, tendem a não usar const com excepção dos parâmetros de referência e de ponteiro. Para objetos copiados isso realmente não importa, embora possa ser mais seguro, uma vez que sinaliza a intenção dentro da função. É realmente um julgamento. Eu tendem a usar const_iterator embora quando looping em algo e eu não pretende modificá-lo, então eu acho que cada um na sua, enquanto precisão const para tipos de referência é rigorosamente mantida.

Outras dicas

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

errada.

Trata-se de auto-documentar seu código e seus pressupostos.

Se o seu código tem muitas pessoas trabalhando nisso e suas funções são não-trivial, então você deve marcar "const" qualquer e tudo o que você pode. Ao escrever código de força industrial, você deve sempre assumir que seus colegas de trabalho são psicopatas tentando levá-lo de qualquer maneira que puder (especialmente desde que é muitas vezes a si mesmo no futuro).

Além disso, como alguém mencionado anteriormente, pode ajuda as coisas compilador otimizar um pouco (apesar de ser um tiro no escuro).

Às vezes (muitas vezes!) Eu tenho que Untangle alguém é código C ++. E todos nós sabemos que de outra pessoa do código C ++ é uma bagunça completa quase por definição :) Portanto, a primeira coisa que faço para decifrar o fluxo de dados local é colocado const em todas as variáveis ??definição até o início do compilador de latir. Isto significa const-qualificar argumentos de valor, bem como, porque eles são apenas fantasia variáveis ??locais inicializado pelo chamador.

Ah, eu desejo variáveis ??foram const por padrão e mutável foi necessário para variáveis ??não-const:)

As duas linhas seguintes são funcionalmente equivalentes:

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

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

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

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

O que isto diz é que foo pode ter um grande parâmetro, talvez uma estrutura de dados que é gigabytes de tamanho, sem copiá-lo. Além disso, ele diz ao interlocutor, "Foo não * alterar o conteúdo desse parâmetro." Passando uma referência const também permite que o compilador para tomar certas decisões de desempenho.

*:. A menos que lança fora o const-ness, mas isso é outro post

const Superfluous extra são ruins a partir de um ponto de vista dos API:

Pondo const supérfluo adicional está em seu código para parâmetros de tipo intrínsecas passados ??por valor clutters sua API ao fazer nenhuma promessa significativa para o usuário chamador ou API (só dificulta a implementação).

'const' Too many em uma API quando não for necessário é como " lobo chorando ", eventualmente, as pessoas vão começar a ignorar 'const' porque é em todo o lugar e não significa nada na maioria das vezes.

O argumento "reductio ad absurdum" para consts extra na API são boas para esses dois primeiros pontos seria é se os parâmetros mais const são boas, então cada argumento de que pode ter um const sobre ele, deve ter um const nele. Na verdade, se fosse realmente tão bom, você iria querer const para ser o padrão para os parâmetros e ter uma palavra-chave como "mutável" somente quando você quiser alterar o parâmetro.

Então, vamos tentar colocar em const onde quer que nós podemos:

void mungerum(char * buffer, const char * mask, int count);

void mungerum(char * const buffer, const char * const mask, const int count);

Considere a linha de código acima. Não só é a declaração mais confuso e mais e mais difícil de ler, mas três dos quatro 'const' palavras-chave pode ser ignorado pelo usuário API. No entanto, o uso extra de 'const' fez a segunda linha potencialmente ! PERIGOSO

Por quê?

A misread rápida da primeira char * const buffer parâmetro pode fazer você pensar que ele não vai modificar a memória buffer de dados que é passado - no entanto, isso não é verdade! Superfluous 'const' pode levar a suposições perigosas e incorretos sobre a sua API quando digitalizada ou mal interpretado rapidamente.


const Superfluous são ruins de uma Implementação suporte de ponto-Code, bem como:

#if FLEXIBLE_IMPLEMENTATION
       #define SUPERFLUOUS_CONST
#else
       #define SUPERFLUOUS_CONST             const
#endif

void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source, SUPERFLUOUS_CONST int count);

Se FLEXIBLE_IMPLEMENTATION não é verdade, então a API é “promissor” para não implementar a função o primeiro caminho a seguir.

void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source, SUPERFLUOUS_CONST int count)
{
       // Will break if !FLEXIBLE_IMPLEMENTATION
       while(count--)
       {
              *dest++=*source++;
       }
}

void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source, SUPERFLUOUS_CONST int count)
{
       for(int i=0;i<count;i++)
       {
              dest[i]=source[i];
       }
}

Isso é uma promessa muito bobo para fazer. Por que você deve fazer uma promessa que lhe dá nenhum benefício em tudo com o seu interlocutor e só limita a sua implementação?

Ambos são implementações perfeitamente válidas da mesma função embora assim que tudo que você fez é amarrado um lado atrás das costas desnecessariamente.

Além disso, é uma promessa muito rasa que é facilmente (e legalmente contornado).

inline void bytecopyWrapped(char * dest,
   const char *source, int count)
{
       while(count--)
       {
              *dest++=*source++;
       }
}
void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source,SUPERFLUOUS_CONST int count)
{
    bytecopyWrapped(dest, source, count);
}

Olha, eu implementado dessa maneira qualquer maneira mesmo que eu prometi não - apenas usando uma função wrapper. É como quando os maus promessas cara não para matar alguém em um filme e ordens de seu capanga de matá-los em seu lugar.

Aqueles de const supérfluos valem mais do que uma promessa de um filme de bad-guy.


Mas a capacidade de mentira fica ainda pior:

I foram iluminados que você pode incompatibilidade const no cabeçalho (declaração) eo código (definição) usando const espúria. Os defensores const-happy reclamar esta é uma coisa boa, uma vez que permite que você coloque const somente na definição.

// Example of const only in definition, not declaration
class foo { void test(int *pi); };
void foo::test(int * const pi) { }

No entanto, o inverso é verdadeiro ... você pode colocar um const espúria apenas na declaração e ignorá-lo na definição. Isto só faz const supérfluo em uma API mais de uma coisa terrível e uma mentira horrível - veja este exemplo:

class foo
{
    void test(int * const pi);
};

void foo::test(int *pi) // Look, the const in the definition is so superfluous I can ignore it here
{
    pi++;  // I promised in my definition I wouldn't modify this
}

Todo o const supérfluo realmente faz é tornar o código do implementador menos legível, forçando-o a usar outra cópia local ou uma função wrapper quando ele quer mudar a variável ou passar a variável por referência não-const.

Veja este exemplo. Que é mais legível? É óbvio que a única razão para a variável extra na segunda função é porque alguns designer de API jogou em um const supérfluo?

struct llist
{
    llist * next;
};

void walkllist(llist *plist)
{
    llist *pnext;
    while(plist)
    {
        pnext=plist->next;
        walk(plist);
        plist=pnext;    // This line wouldn't compile if plist was const
    }
}

void walkllist(llist * SUPERFLUOUS_CONST plist)
{
    llist * pnotconst=plist;
    llist *pnext;
    while(pnotconst)
    {
        pnext=pnotconst->next;
        walk(pnotconst);
        pnotconst=pnext;
    }
}

Esperamos que aprendemos alguma coisa aqui. const supérfluo é uma monstruosidade-desordenar API, um nag irritante, uma rasa e promessa sem sentido, um obstáculo desnecessário e, ocasionalmente, leva a erros muito perigosas.

const deveria ter sido o padrão no C ++. Como esta:

int i = 5 ; // i is a constant

var int i = 5 ; // i is a real variable

Quando eu codificados C ++ para uma vida que eu consted tudo o que eu podia. Utilizando const é uma ótima maneira para ajudar o compilador ajuda você. Por exemplo, const-ing seu método de valores de retorno pode salvá-lo de erros de digitação, tais como:

foo() = 42

quando você significava:

foo() == 42

Se foo () é definido para retornar uma referência não-const:

int& foo() { /* ... */ }

O compilador irá alegremente permitem atribuir um valor à temporário anônimo retornado pela chamada de função. Tornando-const:

const int& foo() { /* ... */ }

elimina essa possibilidade.

Há uma boa discussão sobre este tema no antigo "Guru da Semana" artigos sobre comp.lang.c ++. Moderado aqui .

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

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 número foi const, o compilador iria parar e avisar-nos do bug.

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

Pondo const no parâmetro b boolean no seu exemplo só coloca uma restrição na implementação e não contribui para a interface da classe (embora não alterar parâmetros é geralmente recomendado).

A assinatura da função para

void foo(int a);

e

void foo(const int a);

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

Asaf

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

É o impede de escrever algo como

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

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 tinha colocado um const entre Bar * e p, o compilador teria me dito isso.

Ah, uma pergunta difícil. De um lado, a declaração é um contrato e ele realmente não faz sentido para passar um argumento const por valor. Por outro lado, se você olhar para a implementação da função, você dá o compilador mais chances para otimizar se você declarar uma constante discussão.

parâmetros de valor Marcação 'const' é definitivamente uma coisa subjetiva.

No entanto, eu realmente prefiro parâmetros de valor marca const, assim como no seu exemplo.

void func(const int n, const long l) { /* ... */ }

O valor para mim é em indicando claramente que os valores dos parâmetros de função não são alterados pela função. Eles terão o mesmo valor no início como no final. Para mim, é parte de manter a uma programação muito funcional tipo de estilo.

Por um curto função, é sem dúvida uma perda de tempo / espaço para ter o 'const' lá, uma vez que é geralmente bastante óbvio que os argumentos não são modificados pela função.

No entanto, para uma função maior, é uma forma de documentação de implementação, e é forçado pelo compilador.

posso ter certeza se eu fizer alguma computação com 'n' e 'l', eu posso refatorar / mover que a computação sem medo de obter um resultado diferente porque eu perdi um lugar onde um ou ambos é alterado.

Uma vez que é um detalhe de implementação, você não precisa declarar o valor parâmetros const no cabeçalho, assim como você não precisa declarar os parâmetros da função com os mesmos nomes que os usos de implementação.

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

const deve ser preferido ao passar por referência, a não ser que o objectivo da função é a de modificar o valor transmitido.

const Finalmente, uma função que não modifica objeto atual (presente) pode, e provavelmente deve ser declarado. Um exemplo é a seguir:

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

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

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

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

Pode ser este costuma ser um argumento válido. mas se incrementar o valor de uma variável const dentro de um compilador função nos dará um erro: " Erro: incremento de parâmetro somente leitura ". Então isso significa que podemos usar palavra-chave const como uma forma de evitar que acidentalmente modificar nossas variáveis ??dentro de funções (que não é suposto / somente leitura). Então, se nós acidentalmente fez isso no compilador tempo de compilação vai deixar-nos saber que. isto é especialmente importante se você não é o único que está trabalhando neste projeto.

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

Não é particularmente uma coisa má a fazer, mas os benefícios não são tão grandes, uma vez que ela não afeta a sua API, e acrescenta digitação, por isso não é normalmente feito.

Não uso const para parametere passou de valor. O chamador não se importa se você modificar o parâmetro ou não, é um detalhe de implementação.

O que é realmente importante é marcar métodos como const se não modificar seu exemplo. Faça isso que você vá, porque senão você pode acabar tanto com muita const_cast <> ou você pode achar que marcando uma const método requer mudar um monte de código porque ele chama outros métodos que deveriam ter sido marcados const.

Eu também tendem a marcar vars locais const se eu não precisa modificá-los. Acredito que torna o código mais fácil de entender, tornando-a mais fácil de identificar as "partes móveis".

I tendem a usar const sempre que possível. (Ou outra palavra-chave apropriada para o idioma de destino.) Eu faço isso simplesmente porque ele permite que o compilador para fazer otimizações extras que não seria capaz de fazer o contrário. Como não tenho idéia do que essas otimizações podem ser, eu sempre fazê-lo, mesmo quando parece bobagem.

Para tudo que eu sei, o compilador pode muito bem ver um parâmetro de valor const, e dizer: "Ei, esta função não é modificá-lo de qualquer maneira, para que eu possa passar por referência e salvar alguns ciclos de relógio." Eu não acho que ele nunca faria tal coisa uma, uma vez que altera a assinatura função, mas faz o ponto. Talvez ele faz alguma manipulação pilha diferente ou algo assim ... O ponto é, eu não sei, mas eu sei tentando ser mais esperto do que o compilador só leva a me sendo envergonhado.

C ++ tem alguma bagagem extra, com a idéia de const-correção, por isso se torna ainda mais importante.

Em otimizações do compilador: http://www.gotw.ca/gotw/081.htm

Eu uso const foram posso. Const para parâmetros significa que eles não devem mudar seu valor. Isto é especialmente importante quando passar por referência. const para a função declara que a função não deve mudar as classes de membros.

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 ele contém um membro de referência - não um problema para tipos built-in ). Se o parâmetro é uma referência ou ponteiro, geralmente é melhor para proteger a referenciado / apontado para a memória, não o ponteiro em si (eu acho que você não pode fazer a própria referência const, não que isso importe tanto quanto você não pode mudar o árbitro) . Parece uma boa idéia para tudo protegido Pode como const. Você pode omiti-lo sem medo de cometer um erro se os parâmetros forem PODs apenas (incluindo tipos built-in) e não há chance deles mudando ainda mais ao longo da estrada (por exemplo, no seu exemplo o parâmetro bool).

Eu não sabia sobre o .h / .cpp diferença declaração de arquivo, mas faz algum sentido. No nível código de máquina, nada é "const", por isso, se você declarar uma função (no .h) como não-const, o código é o mesmo que se você declará-lo como const (otimizações de lado). No entanto, ele ajuda você a se alistar ao compilador que você não vai alterar o valor da variável dentro da implementação da função (.ccp). Pode vir a calhar no caso quando você está herdando de uma interface que permite a mudança, mas você não precisa alterar o parâmetro para alcançar a funcionalidade necessária.

Para resumir:

  • "Normalmente const passagem por valor é unuseful e enganosa na melhor das hipóteses." De GOTW006
  • Mas você pode adicioná-los na .cpp como você faria com 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.

Eu não colocaria const em parâmetros como que - todo mundo já sabe que um booleano (em oposição a um booleano &) é constante, por isso, adicionando-o vai fazer as pessoas pensar "? Espera, o que" ou mesmo que você está passando o parâmetro por referência.

A coisa a lembrar com const é que é muito mais fácil fazer coisas const desde o início, do que está a tentar colocá-los mais tarde.

Use const quando você quer algo a ser inalterado - a uma sugestão adicionada que descreve o que a função faz e o que esperar. Eu vi muitos uma API C que poderia fazer com alguns deles, especialmente aqueles que aceitam strings C!

Eu estaria mais inclinado a omitir a palavra-chave const no arquivo cpp que o cabeçalho, mas como eu tendem a corte + colá-los, eles seriam mantidos em ambos os lugares. Eu não tenho idéia por que o compilador permite que, eu acho que é uma coisa compilador. A melhor prática é definitivamente para colocar a sua palavra-chave const em ambos os arquivos.

Não há realmente nenhuma razão para fazer um parâmetro de valor "const" como a função só pode modificar uma cópia da variável de qualquer maneira.

A razão para usar "const" é se você estiver passando algo maior (por exemplo, uma estrutura com muitos membros) por referência, caso em que ele garante que a função não pode modificá-lo; ou melhor, o compilador vai reclamar se você tentar modificá-lo da maneira convencional. Ele impede que ele seja acidentalmente modificado.

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

Como parâmetros são passados ??por valor, ele não faz nenhuma diferença se você especificar const ou não de perspective.It da função de chamada, basicamente, não faz qualquer sentido para passagem declarar, por parâmetros de valor como const.

Todos os consts em seus exemplos não têm nenhum propósito. C ++ é passar por valor, por padrão, de modo que a função obtém cópias desses inteiros e booleanos. Mesmo que a função faz modificá-los, cópia do chamador não é afetado.

Então, eu gostaria de evitar consts extras porque

  • Eles são redundantes
  • Eles atravancam o texto
  • Eles me impedir de Alterando o valor passado em casos em que ele pode ser útil ou eficiente.

Eu sei que a questão é "um pouco" desatualizado, mas como eu deparei-lo outra pessoa também pode fazê-lo no futuro ... ... ainda duvido que o pobre sujeito irá listar aqui para ler o meu comentário:)

Parece-me que ainda estamos muito confinado a C-estilo maneira de pensar. No paradigma OOP que brincar com objetos, não tipos. objecto constante pode ser conceptualmente diferente a partir de um objecto não-const, especificamente no sentido de lógica-const (em contraste com o bit a bit-const). Assim, mesmo que const correção dos parâmetros de função é (talvez) um excesso de cuidado em caso de PODs não é assim no caso de objetos. Se uma função trabalha com um objeto const deve dizer isso. Considere o seguinte trecho de código

#include <iostream>

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class SharedBuffer {
private:

  int fakeData;

  int const & Get_(int i) const
  {

    std::cout << "Accessing buffer element" << std::endl;
    return fakeData;

  }

public:

  int & operator[](int i)
  {

    Unique();
    return const_cast<int &>(Get_(i));

  }

  int const & operator[](int i) const
  {

    return Get_(i);

  }

  void Unique()
  {

    std::cout << "Making buffer unique (expensive operation)" << std::endl;

  }

};

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void NonConstF(SharedBuffer x)
{

  x[0] = 1;

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ConstF(const SharedBuffer x)
{

  int q = x[0];

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int main()
{

  SharedBuffer x;

  NonConstF(x);

  std::cout << std::endl;

  ConstF(x);

  return 0;

}

ps .: você pode argumentar que (const) de referência seria mais apropriado aqui e dá-lhe o mesmo comportamento. Bem, certo. Apenas dando uma imagem diferente do que eu podia ver em outro lugar ...

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

Claro que o programa lhe diz, gerando um erro de exceção na linha onde cometeu o erro, mas então você precisa adivinhar qual dos 2-10 parâmetros é errado.

Então agora eu tenho a tarefa desagradável de tentar convencer um desenvolvedor que eles realmente deve definir suas variáveis ??(no arquivo .h) de uma forma que permite um método automatizado de criar todas as definições de funções VB.NET facilmente. Eles, então, presunçosamente dizer "dizia o ... 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 / S vs R / W, ele só faz metade do trabalho.

EDIT:

No encorajamento de outro usuário que estou adicionando a seguinte;

Aqui é um exemplo de um (OMI) entrada .h mal formadas;

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 o "const" em falta no primeiro parâmetro. Sem ele, um programa (ou outro desenvolvedor) não tem idéia do 1º parâmetro deve ser passado "ByVal". Ao adicionar o "const" faz o auto ficheiro.h documentar para que os desenvolvedores usam outras línguas pode facilmente escrever código de trabalho.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top