const (programação de computador) - const (computer programming)

Nos C , C ++ , D , JavaScript e Julia linguagens de programação , const é um qualificador tipo : a palavra-chave aplicada a um tipo de dados que indica que os dados é somente leitura. Embora isso possa ser usado para declarar constantes , const na família C de linguagens difere de construções semelhantes em outras linguagens por ser parte do tipo e , portanto, tem um comportamento complicado quando combinado com ponteiros , referências, tipos de dados compostos e verificação de tipo.

Introdução

Quando aplicado na declaração de um objeto , indica que o objeto é uma constante : seu valor não pode ser alterado, ao contrário de uma variável . Esse uso básico - para declarar constantes - tem paralelos em muitas outras linguagens.

No entanto, ao contrário de outras linguagens, na família de linguagens C, o const é parte do tipo , não parte do objeto . Por exemplo, em C, declara um objeto do tipo - o é parte do tipo, como se fosse analisado "(int const) x" - enquanto em Ada , declara uma constante (um tipo de objeto) do tipo: o é parte do objeto , mas não parte do tipo . int const x = 1;xint constconstX : constant INTEGER := 1_XINTEGERconstant

Isso tem dois resultados sutis. Em primeiro lugar, const pode ser aplicado a partes de um tipo mais complexo - por exemplo, int const * const x; declara um ponteiro constante para um inteiro constante, enquanto int const * x; declara um ponteiro variável para um inteiro constante e int * const x; declara um ponteiro constante para um inteiro variável. Em segundo lugar, como const faz parte do tipo, deve corresponder como parte da verificação de tipo. Por exemplo, o código a seguir é inválido:

void f(int& x);
// ...
int const i;
f(i);

porque o argumento para f deve ser um inteiro variável , mas i é um inteiro constante . Essa correspondência é uma forma de correção do programa e é conhecida como correção constante . Isso permite uma forma de programação por contrato , onde as funções especificam como parte de sua assinatura de tipo se eles modificam seus argumentos ou não, e se seu valor de retorno é modificável ou não. Essa verificação de tipo é principalmente de interesse em ponteiros e referências - não em tipos de valor básicos como inteiros - mas também em tipos de dados compostos ou tipos de modelo, como contêineres . É oculto pelo fato de que const muitas vezes pode ser omitido, devido à coerção de tipo ( conversão de tipo implícita ) e C sendo chamada por valor (C ++ e D são chamadas por valor ou chamada por referência).

Consequências

A ideia de constância não implica que a variável, conforme é armazenada na memória do computador, não seja gravável. Em vez disso, const -ness é uma construção de tempo de compilação que indica o que um programador deve fazer, não necessariamente o que ele pode fazer. Note-se, no entanto, que, no caso de dados pré-definido (tais como char const * cadeia de caracteres literais ), C const é muitas vezes não gravável.

Distinção de constantes

Embora uma constante não mude seu valor enquanto o programa está sendo executado, um objeto declarado const pode realmente alterar seu valor enquanto o programa está sendo executado. Um exemplo comum são os registros somente leitura em sistemas embarcados, como o estado atual de uma entrada digital. Os registros de dados para entradas digitais são freqüentemente declarados como const e volatile . O conteúdo desses registros pode mudar sem que o programa faça nada ( volatile ), mas você também não deve escrever neles ( const ).

Outros usos

Além disso, uma função-membro (não estática) pode ser declarada como const . Nesse caso, o this ponteiro dentro de tal função é do tipo object_type const * e não apenas do tipo object_type * . Isso significa que funções não const para este objeto não podem ser chamadas de dentro de tal função, nem podem ser modificadas variáveis ​​de membro . Em C ++, uma variável de membro pode ser declarada como mutable , indicando que essa restrição não se aplica a ela. Em alguns casos, isso pode ser útil, por exemplo, com armazenamento em cache , contagem de referência e sincronização de dados . Nestes casos, o significado lógico (estado) do objeto permanece inalterado, mas o objeto não é fisicamente constante, pois sua representação bit a bit pode mudar.

Sintaxe

Em C, C ++ e D, todos os tipos de dados, incluindo aqueles definidos pelo usuário, podem ser declarados const , e const-correção determina que todas as variáveis ​​ou objetos devem ser declarados como tais, a menos que precisem ser modificados. Esse uso proativo de const torna os valores "mais fáceis de entender, rastrear e raciocinar" e, portanto, aumenta a legibilidade e a compreensibilidade do código e torna o trabalho em equipes e a manutenção do código mais simples, pois comunica informações sobre o uso pretendido de um valor. Isso pode ajudar o compilador e também o desenvolvedor a raciocinar sobre o código. Ele também pode permitir que um compilador otimizado gere um código mais eficiente.

Tipos de dados simples

Para tipos de dados não-ponteiro simples, a aplicação do const qualificador é direta. Ele pode estar em qualquer lado de alguns tipos por razões históricas (por exemplo, const char foo = 'a'; é equivalente a char const foo = 'a'; ). Em algumas implementações, usar const duas vezes (por exemplo, const char const ou char const const ) gera um aviso, mas não um erro.

Ponteiros e referências

Para os tipos de ponteiro e referência, o significado de const é mais complicado - o próprio ponteiro ou o valor sendo apontado, ou ambos, podem ser const . Além disso, a sintaxe pode ser confusa. Um ponteiro pode ser declarado como um const ponteiro para um valor gravável, um ponteiro gravável para um const valor ou um const ponteiro para um const valor. Um const ponteiro não pode ser reatribuído para apontar para um objeto diferente daquele para o qual foi inicialmente atribuído, mas pode ser usado para modificar o valor para o qual ele aponta (chamado de ponta ). Variáveis ​​de referência em C ++ são uma sintaxe alternativa para const ponteiros. Um ponteiro para um const objeto, por outro lado, pode ser reatribuído para apontar para outro local da memória (que deve ser um objeto do mesmo tipo ou de tipo conversível), mas não pode ser usado para modificar a memória que está apontando para. Um const ponteiro para um const objeto também pode ser declarado e não pode ser usado para modificar a ponteira nem ser reatribuído para apontar para outro objeto. O código a seguir ilustra essas sutilezas:

void Foo( int * ptr,
          int const * ptrToConst,
          int * const constPtr,
          int const * const constPtrToConst )
{
    *ptr = 0; // OK: modifies the "pointee" data
    ptr  = NULL; // OK: modifies the pointer

    *ptrToConst = 0; // Error! Cannot modify the "pointee" data
    ptrToConst  = NULL; // OK: modifies the pointer

    *constPtr = 0; // OK: modifies the "pointee" data
    constPtr  = NULL; // Error! Cannot modify the pointer

    *constPtrToConst = 0; // Error! Cannot modify the "pointee" data
    constPtrToConst  = NULL; // Error! Cannot modify the pointer
}

Convenção C

Seguindo a convenção C usual para declarações, a declaração segue o uso, e o * em um ponteiro é escrito no ponteiro, indicando desreferenciamento . Por exemplo, na declaração int *ptr , o formulário não referenciado *ptr é um int , enquanto o formulário de referência ptr é um ponteiro para um int . Assim, const modifica o nome à sua direita. A convenção C ++ é, em vez disso, associar o * com o tipo, como em int* ptr, e ler o const como modificando o tipo à esquerda. int const * ptrToConst pode, portanto, ser lido como " *ptrToConst é um int const " (o valor é constante) ou " ptrToConst é um int const * " (o ponteiro é um ponteiro para um número inteiro constante). Portanto:

int *ptr; // *ptr is an int value
int const *ptrToConst; // *ptrToConst is a constant (int: integer value)
int * const constPtr; // constPtr is a constant (int *: integer pointer)
int const * const constPtrToConst; // constPtrToConst is a constant (pointer)
                                   // as is *constPtrToConst (value)

Convenção C ++

Seguindo a convenção C ++ de análise do tipo, não do valor, uma regra geral é ler a declaração da direita para a esquerda. Assim, tudo à esquerda da estrela pode ser identificado como o tipo de ponta e tudo à direita da estrela são as propriedades do ponteiro. Por exemplo, em nosso exemplo acima, int const * pode ser lido como um ponteiro gravável que se refere a um inteiro não gravável e int * const pode ser lido como um ponteiro não gravável que se refere a um inteiro gravável.

Uma regra mais genérica que ajuda você a entender declarações e definições complexas funciona assim:

  1. encontre o identificador cuja declaração você deseja entender
  2. leia tanto quanto possível à direita (ou seja, até o final da declaração ou para o próximo parêntese de fechamento, o que ocorrer primeiro)
  3. de volta para onde você começou, e leia para trás para a esquerda (ou seja, até o início da declaração ou para o parêntese aberto que corresponde ao parêntese de fechamento encontrado na etapa anterior)
  4. quando você atingiu o início da declaração, está feito. Caso contrário, continue na etapa 2, além do parêntese de fechamento que foi correspondido por último.

Aqui está um exemplo:

Parte da expressão
double (**const (*fun(int))(double))[10]
Significado
(lendo para baixo)
Identificador
                  fun
fun é um ...
Leia à direita
                     (int))
função esperando um int ...
Encontre a correspondência (
                (*
retornando um ponteiro para ...
Continue à direita
                           (double))
uma função esperando um duplo ...
Encontre a correspondência (
        (**const
retornando um ponteiro constante para
um ponteiro para ...
Continue à direita
                                    [10]
blocos de 10 ...
Leia para a esquerda
double
duplica.

Ao ler para a esquerda, é importante que você leia os elementos da direita para a esquerda. Portanto, an int const * torna - se um ponteiro para const int e não um ponteiro const para um int .

Em alguns casos, C / C ++ permite que a const palavra-chave seja colocada à esquerda do tipo. aqui estão alguns exemplos:

const int *ptrToConst;            //identical to: int const *ptrToConst,
const int *const constPtrToConst; //identical to: int const *const constPtrToConst

Embora C / C ++ permita tais definições (que se aproximam do idioma inglês ao ler as definições da esquerda para a direita), o compilador ainda lê as definições de acordo com o procedimento mencionado acima: da direita para a esquerda. Mas colocar const antes o que deve ser constante rapidamente introduz incompatibilidades entre o que você pretende escrever e o que o compilador decide que você escreveu. Considere ponteiros para ponteiros:

int **ptr;            // a pointer to a pointer to ints
int const **ptr       // a pointer to a pointer to constant int value
                      // (not a pointer to a constant pointer to ints)
int *const *ptr       // a pointer to a const pointer to int values
                      // (not a constant pointer to a pointer to ints)
int **const ptr       // a constant pointer to pointers to ints
                      // (ptr, the identifier, being const makes no sense)
int const **const ptr // a constant pointer to pointers to constant int values

Como uma nota final sobre as definições do ponteiro: sempre escreva o símbolo do ponteiro (o *) o máximo possível à direita. Anexar o símbolo de ponteiro ao tipo é complicado, pois sugere fortemente um tipo de ponteiro, o que não é o caso. aqui estão alguns exemplos:

int* a;          /* write: */     int *a;    // a is a pointer to an int
int* a, b;       // CONFUSING 
                 /* write: */     int *a, b; // a is a pointer to an int, 
                 //                             but b is a mere int
int* a, *b;      // UGLY: both a and b are pointers to ints
                 /* write: */     int *a, *b;

O FAQ de Bjarne Stroustrup recomenda declarar apenas uma variável por linha se usar a convenção C ++, para evitar este problema.

As mesmas considerações se aplicam à definição de referências e referências rvalue:

int var = 22;
int const &refToConst = var;         // OK
int const& ref2 = var, ref3 = var;   // CONFUSING:
                                     // ref2 is a reference, but ref3 isn't:
                                     // ref3 is a constant int initialized with
                                     // var's value
int &const constRef = var;           // ERROR: as references can't change anyway.

// C++:
int&& rref = int(5), value = 10;     // CONFUSING:
                                     // rref is an rvalue reference, but value is
                                     // a mere int. 
                                     /* write: */ int &&rref = int(5), value = 10;

Declarações mais complicadas são encontradas ao usar matrizes multidimensionais e referências (ou ponteiros) para ponteiros. Embora às vezes seja argumentado que tais declarações são confusas e sujeitas a erros e que, portanto, devem ser evitadas ou substituídas por estruturas de nível superior, o procedimento descrito no início desta seção pode sempre ser usado sem introduzir ambigüidades ou confusão.

Parâmetros e variáveis

const pode ser declarado tanto em parâmetros de função quanto em variáveis ​​( estáticas ou automáticas, incluindo globais ou locais). A interpretação varia entre os usos. Uma const variável estática (variável global ou variável local estática) é uma constante e pode ser usada para dados como constantes matemáticas, como double const PI = 3.14159 - realisticamente mais, ou parâmetros gerais de tempo de compilação. Uma const variável automática (variável local não estática) significa que uma atribuição única está acontecendo, embora um valor diferente possa ser usado a cada vez, como int const x_squared = x * x . Um const parâmetro na passagem por referência significa que o valor referenciado não é modificado - faz parte do contrato - enquanto um const parâmetro na passagem por valor (ou o próprio ponteiro, na passagem por referência) não adiciona nada para a interface (pois o valor foi copiado), mas indica que internamente a função não modifica a cópia local do parâmetro (é uma atribuição única). Por esse motivo, alguns preferem usar const em parâmetros apenas para passagem por referência, onde muda o contrato, mas não para passagem por valor, onde expõe a implementação.

C ++

Métodos

Para tirar proveito da abordagem de design by contract para tipos definidos pelo usuário (structs e classes), que podem ter métodos, bem como dados de membros, o programador pode marcar métodos de instância como const se eles não modificassem os membros de dados do objeto. Aplicando o const qualificador para métodos de instância, portanto, é uma característica essencial para const-correção, e não está disponível em muitas outras orientadas a objeto linguagens como Java e C # ou em Microsoft 's C ++ / CLI ou extensões geridas para C ++ . Enquanto os const métodos podem ser chamados por const e não- const objetos da mesma forma, os não- const métodos só podem ser chamados por não- const objetos. O const modificador em um método de instância se aplica ao objeto apontado pelo this ponteiro " ", que é um argumento implícito passado para todos os métodos de instância. Portanto, ter const métodos é uma forma de aplicar correção constante ao this argumento " " ponteiro implícito, assim como outros argumentos.

Este exemplo ilustra:

class C
{
    int i;
public:
    int Get() const // Note the "const" tag
      { return i; }
    void Set(int j) // Note the lack of "const"
      { i = j; }
};

void Foo(C& nonConstC, C const& constC)
{
    int y = nonConstC.Get(); // Ok
    int x = constC.Get();    // Ok: Get() is const

    nonConstC.Set(10); // Ok: nonConstC is modifiable
    constC.Set(10);    // Error! Set() is a non-const method and constC is a const-qualified object
}

No código acima, o this ponteiro " " implícito para Set() tem o tipo " C *const "; enquanto o this ponteiro " " para Get() tem o tipo " C const *const ", indicando que o método não pode modificar seu objeto através do this ponteiro " ".

Freqüentemente, o programador fornecerá um método const e um não- const método com o mesmo nome (mas possivelmente com usos bem diferentes) em uma classe para acomodar os dois tipos de chamadores. Considerar:

class MyArray
{
    int data[100];
public:
    int &       Get(int i)       { return data[i]; }
    int const & Get(int i) const { return data[i]; }
};

void Foo( MyArray & array, MyArray const & constArray )
{
    // Get a reference to an array element
    // and modify its referenced value.

    array.Get( 5 )      = 42; // OK! (Calls: int & MyArray::Get(int))
    constArray.Get( 5 ) = 42; // Error! (Calls: int const & MyArray::Get(int) const)
}

O const -ness do objeto de chamada determina qual versão de MyArray::Get() será invocada e, portanto, se o chamador recebe ou não uma referência com a qual ele pode manipular ou apenas observar os dados privados no objeto. Os dois métodos têm, tecnicamente, assinaturas diferentes porque seus " this " ponteiros têm tipos diferentes, permitindo que o compilador escolha o correto. (Retornar uma const referência para um int , em vez de simplesmente retornar o int valor por, pode ser um exagero no segundo método, mas a mesma técnica pode ser usada para tipos arbitrários, como na Biblioteca de Modelos Padrão .)

Brechas para const-correção

Existem várias lacunas para a correção const pura em C e C ++. Eles existem principalmente para compatibilidade com o código existente.

O primeiro, que se aplica apenas ao C ++, é o uso de const_cast , que permite ao programador remover o const qualificador, tornando qualquer objeto modificável. A necessidade de remover o qualificador surge ao usar código e bibliotecas existentes que não podem ser modificados, mas que não são constantemente corretos. Por exemplo, considere este código:

// Prototype for a function which we cannot change but which
// we know does not modify the pointee passed in.
void LibraryFunc(int* ptr, int size);

void CallLibraryFunc(int const * ptr, int size)
{
    LibraryFunc(ptr, size); // Error! Drops const qualifier

    int* nonConstPtr = const_cast<int*>(ptr); // Strip qualifier
    LibraryFunc(nonConstPtr, size);  // OK
}

No entanto, qualquer tentativa de modificar um objeto que é declarado const por meio de um cast const resulta em um comportamento indefinido de acordo com o padrão ISO C ++. No exemplo acima, se fizer ptr referência a uma variável global, local ou de membro declarada como const , ou um objeto alocado no heap por meio new int const , o código só estará correto se LibraryFunc realmente não modificar o valor apontado por ptr .

A linguagem C precisa de uma brecha porque existe uma certa situação. Variáveis ​​com duração de armazenamento estático podem ser definidas com um valor inicial. No entanto, o inicializador pode usar apenas constantes como constantes de string e outros literais, e não tem permissão para usar elementos não constantes como nomes de variáveis, sejam os elementos do inicializador declarados const ou não, ou se a variável de duração estática está sendo declarada const ou não. Existe uma maneira não portátil de inicializar uma const variável que tem duração de armazenamento estático. Construindo cuidadosamente um typecast no lado esquerdo de uma atribuição posterior, uma const variável pode ser escrita, removendo efetivamente o const atributo e 'inicializando-o' com elementos não constantes como outras const variáveis ​​e outros. Escrever em uma const variável dessa maneira pode funcionar conforme o esperado, mas causa um comportamento indefinido e contradiz seriamente a correção constante:

size_t const    bufferSize = 8*1024;
size_t const    userTextBufferSize;  //initial value depends on const bufferSize, can't be initialized here

...

int setupUserTextBox(textBox_t *defaultTextBoxType, rect_t *defaultTextBoxLocation)
{
    *(size_t*)&userTextBufferSize = bufferSize - sizeof(struct textBoxControls);  // warning: might work, but not guaranteed by C
    ...
}

Outra lacuna se aplica a C e C ++. Especificamente, as linguagens determinam que as referências de membros e referências são "superficiais" no que diz respeito ao const -ness de seus proprietários - isto é, um objeto que const contém todos os const membros, exceto que as referências de membros (e árbitros) ainda são mutáveis. Para ilustrar, considere este código C ++:

struct S
{
    int val;
    int *ptr;
};

void Foo(S const & s)
{
    int i  = 42;
    s.val  = i;  // Error: s is const, so val is a const int
    s.ptr  = &i; // Error: s is const, so ptr is a const pointer to int
    *s.ptr = i;  // OK: the data pointed to by ptr is always mutable,
                 //     even though this is sometimes not desirable
}

Embora o objeto s passado Foo() seja constante, o que torna todos os seus membros constantes, a ponta acessível por meio s.ptr ainda é modificável, embora isso possa não ser desejável do ponto de vista da const correção, porque s pode possuir apenas a ponta. Por esse motivo, Meyers argumenta que o padrão para ponteiros de membro e referências deve ser "deep" const -ness, que pode ser substituído por um mutable qualificador quando o ponteiro não pertence ao contêiner, mas essa estratégia criaria problemas de compatibilidade com o código existente. Assim, por razões históricas, essa lacuna permanece aberta em C e C ++.

A última lacuna pode ser fechada usando uma classe para ocultar o ponteiro atrás de uma const interface correta, mas tais classes não suportam a semântica de cópia usual de um const objeto (implicando que a classe contida não pode ser copiada pela semântica usual) ou permitir outras lacunas, permitindo a eliminação de const -ness por meio de cópia inadvertida ou intencional.

Finalmente, várias funções na biblioteca padrão C violam a correção const, pois aceitam um const ponteiro para uma string de caracteres e retornam um não- const ponteiro para uma parte da mesma string. strtol e strchr estão entre essas funções. Algumas implementações da biblioteca padrão C ++, como a da Microsoft, tentam fechar essa lacuna fornecendo duas versões sobrecarregadas de algumas funções: uma const versão " " e uma versão "não const ".

Problemas

O uso do sistema de tipos para expressar constância leva a várias complexidades e problemas e, portanto, foi criticado e não adotado fora da estreita família C de C, C ++ e D. Java e C #, que são fortemente influenciados por C e C ++, ambos rejeitaram explicitamente os const qualificadores de tipo de estilo, em vez de expressar constância por palavras-chave que se aplicam ao identificador ( final em Java const e readonly em C #). Mesmo dentro de C e C ++, o uso de const varia significativamente, com alguns projetos e organizações usando-o de forma consistente e outros evitando.

strchr problema

O const qualificador de tipo causa dificuldades quando a lógica de uma função é agnóstica se sua entrada é constante ou não, mas retorna um valor que deve ser do mesmo tipo qualificado de uma entrada. Em outras palavras, para essas funções, se a entrada for constante (qualificada const), o valor de retorno também deve ser, mas se a entrada for variável (não qualificada), o valor de retorno também const deverá ser. Como a assinatura de tipo dessas funções é diferente, ela requer duas funções (ou potencialmente mais, no caso de várias entradas) com a mesma lógica - uma forma de programação genérica .

Esse problema surge até mesmo para funções simples na biblioteca padrão C, notavelmente strchr ; esta observação é creditada por Ritchie a Tom Plum em meados dos anos 1980. A strchr função localiza um caractere em uma string; formalmente, ele retorna um ponteiro para a primeira ocorrência do caractere c na string s , e no C clássico (K&R C) seu protótipo é:

char *strchr(char *s, int c);

A strchr função não modifica a string de entrada, mas o valor de retorno é frequentemente usado pelo chamador para modificar a string, como:

if (p = strchr(q, '/'))
    *p = ' ';

Assim, por um lado, a string de entrada pode ser const (desde que não seja modificada pela função), e se a string de entrada for const o valor de retorno também deve ser - simplesmente porque pode retornar exatamente o ponteiro de entrada, se o primeiro caractere é uma correspondência - mas por outro lado, o valor de retorno não deveria ser const se a string original não fosse const , uma vez que o chamador pode desejar usar o ponteiro para modificar a string original.

Em C ++, isso é feito por meio de sobrecarga de função , geralmente implementada por meio de um modelo , resultando em duas funções, de modo que o valor de retorno tenha o mesmo const tipo -qualificado da entrada:

char* strchr(char* s, int c);
char const* strchr(char const* s, int c);

Estes, por sua vez, podem ser definidos por um modelo:

template <T>
T* strchr(T* s, int c) { ... }

Em D, isso é tratado por meio da inout palavra - chave, que atua como um caractere curinga para const, imutável ou não qualificado (variável), resultando em:

inout(char)* strchr(inout(char)* s, int c);

No entanto, em C nenhum desses é possível, pois C não tem sobrecarga de função e, em vez disso, isso é tratado por ter uma única função em que a entrada é constante, mas a saída é gravável:

char *strchr(char const *s, int c);

Isso permite código C idiomático, mas remove o qualificador const se a entrada realmente for qualificada por const, violando a segurança de tipo. Esta solução foi proposta por Ritchie e posteriormente adotada. Essa diferença é uma das falhas de compatibilidade de C e C ++ .

D

Na versão 2 da linguagem de programação D , existem duas palavras-chave relacionadas a const. A immutable palavra-chave denota dados que não podem ser modificados por meio de nenhuma referência. A const palavra-chave denota uma visão não mutável de dados mutáveis. Ao contrário de C ++ const , D const e immutable são "profunda" ou transitória , e nada acessível através de uma const ou immutable objecto é const ou immutable respectivamente.

Exemplo de const vs. imutável em D

int[] foo = new int[5];  // foo is mutable.
const int[] bar = foo;   // bar is a const view of mutable data.
immutable int[] baz = foo;  // Error:  all views of immutable data must be immutable.

immutable int[] nums = new immutable(int)[5];  // No mutable reference to nums may be created.
const int[] constNums = nums;  // Works.  immutable is implicitly convertible to const.
int[] mutableNums = nums;  // Error:  Cannot create a mutable view of immutable data.

Exemplo de const transitivo ou profundo em D

class Foo {
    Foo next;
    int num;
}

immutable Foo foo = new immutable(Foo);
foo.next.num = 5;  // Won't compile.  foo.next is of type immutable(Foo).
                   // foo.next.num is of type immutable(int).

História

const foi introduzido por Bjarne Stroustrup em C com Classes , o predecessor do C ++ , em 1981, e foi originalmente chamado readonly . Quanto à motivação, Stroustrup escreve:

“Ele tinha duas funções: como forma de definir uma constante simbólica que obedece a regras de escopo e tipo (ou seja, sem usar uma macro) e como forma de considerar um objeto na memória imutável.”

O primeiro uso, como alternativa com escopo e digitação para macros, foi cumprido analogamente para macros do tipo função por meio da inline palavra - chave. Os ponteiros constantes e a * const notação foram sugeridos por Dennis Ritchie e assim adotados.

const foi então adotado em C como parte da padronização e aparece em C89 (e versões subsequentes) junto com o outro qualificador de tipo volatile ,. Um qualificador adicional noalias ,, foi sugerido na reunião de dezembro de 1987 do comitê X3J11, mas foi rejeitado; sua meta foi finalmente cumprida pela restrict palavra - chave em C99 . Ritchie não apoiou muito esses acréscimos, argumentando que eles não "carregavam seu peso", mas, em última análise, não defendeu sua remoção do padrão.

D subsequentemente herdou const de C ++, onde é conhecido como um construtor de tipo (não qualificador de tipo ) e adicionou dois outros construtores de tipo, immutable e inout , para lidar com casos de uso relacionados.

Outras línguas

Outras linguagens não seguem C / C ++ por terem constância parte do tipo, embora muitas vezes tenham construções superficialmente semelhantes e possam usar a const palavra - chave. Normalmente, isso é usado apenas para constantes (objetos constantes).

C # tem uma const palavra - chave, mas com uma semântica radicalmente diferente e mais simples: significa uma constante de tempo de compilação e não faz parte do tipo.

Nim tem uma const palavra - chave semelhante à do C #: ele também declara uma constante de tempo de compilação em vez de fazer parte do tipo. No entanto, no Nim, uma constante pode ser declarada a partir de qualquer expressão que pode ser avaliada em tempo de compilação. Em C #, apenas os tipos internos de C # podem ser declarados como const ; tipos definidos pelo usuário, incluindo classes, estruturas e matrizes, não podem ser const .

Java não tem const - em vez disso final , tem , o que pode ser aplicado a declarações de "variáveis" locais e se aplica ao identificador, não ao tipo. Ele tem um uso orientado a objetos diferente para membros de objetos, que é a origem do nome.

A especificação da linguagem Java é considerada const uma palavra-chave reservada - ou seja, uma que não pode ser usada como identificador de variável - mas não atribui semântica a ela: é uma palavra reservada (não pode ser usada em identificadores), mas não uma palavra - chave (não tem especial significado). Pensa-se que a reserva da palavra-chave ocorreu para permitir uma extensão da linguagem Java para incluir const métodos de estilo C ++ e ponteiro para const tipo. Um tíquete de solicitação de aprimoramento para implementar a const correção existe no Java Community Process , mas foi fechado em 2005 com base na impossibilidade de implementar de maneira compatível com versões anteriores.

O Ada 83 contemporâneo tinha independentemente a noção de um objeto constante e uma constant palavra - chave, com parâmetros de entrada e parâmetros de loop sendo implicitamente constantes. Aqui, o constant é uma propriedade do objeto, não do tipo.

JavaScript tem uma const declaração que define uma variável com escopo de bloco que não pode ser reatribuída nem redeclarada. Ele define uma referência somente leitura para uma variável que não pode ser redefinida, mas em algumas situações o valor da própria variável pode potencialmente mudar, como se a variável se referisse a um objeto e uma propriedade dele fosse alterada.

Veja também

Notas

Referências

links externos