Norma C++

const exatidão

o que é “exatidão const”?

uma coisa boa. Significa usar a palavra-chave const para evitar que objetos const sofram mutação.

Por exemplo, se você quiser criar uma função f() que aceitou um std::string, e você deseja a promessa callersnot para alterar a chamada std::string que é passada para f(), você pode ter f() receber o seu std::stringparâmetro…

  • void f1(const std::string& s); // Passagem por referência para-const
  • void f2(const std::string* sptr); // Passe pelo ponteiro-para-const
  • void f3(std::string s); // Passagem por valor

Na passagem por referência-para-const e passe pelo ponteiro-para-const casos, qualquer tentativa de alterar a chamadastd::string dentro do f() funções seriam sinalizados pelo compilador como um erro na tempo de compilação. Esta verificação é feita inteiramente no tempo de compilação: não existe espaço de tempo de execução ou custo de velocidade para o const. In the pass by value case (f3()), the called function gets a copy of the caller’s std::string. Isto significa que f3() pode alterar a sua localização, mas a cópia é destruída quando f3() retorna. In particular f3() cannot change the caller’s std::stringobject.

como um exemplo oposto, suponha que você queria criar uma função g() que aceitasse um std::string, mas você quer que os chamadores saibam que g() pode mudar o objeto do chamador std::string. Neste caso, você pode ter g() receber o seustd::string parâmetro…

  • void g1(std::string& s); // Passagem por referência a não-const
  • void g2(std::string* sptr); // Passe pelo ponteiro-para-não-const

A falta do const nessas funções informa ao compilador que é permitido (mas não obrigatório) alterar thecaller do std::string objeto. Assim, eles podem passar seus std::string para qualquer uma das funções f(), mas apenas f3()(aquele que recebe seu parâmetro “por valor”) pode passar seus std::string para g1() ou g2(). Se f1() ou f2()precisam chamar a função g() , uma cópia local do objeto std::string deve ser passada para a função g(); o parâmetro para f1() ou f2() não pode ser diretamente passada para a função g(). E. g.,

void g1(std::string& s);void f1(const std::string& s){ g1(s); // Compile-time Error since s is const std::string localCopy = s; g1(localCopy); // Okay since localCopy is not const}

Naturalmente, no caso acima, quaisquer alterações que g1() faz com que são feitas para o localCopy objeto que é o local para f1().Em particular, não serão efectuadas alterações para o const parâmetro que foi passado por referência para f1().

como é que o” const correctness ” está relacionado com a segurança normal do tipo?

declarar a const – nidade de um parâmetro é apenas outra forma de segurança do tipo.

se achar que a segurança normal do tipo o ajuda a corrigir os sistemas (ele faz; especialmente em sistemas grandes), encontraráconst a correcção também ajuda.

o benefício da correcção de const é que o impede de alterar inadvertidamente algo que não esperava que fosse modificado. Você acaba precisando decorar seu código com algumas teclas extras (a palavra— chave const), com a vantagem de que você está dizendo ao compilador e outros programadores alguma parte adicional de informações semânticas importantes-informações que o compilador usa para prevenir erros e outros programadores usam como documentação.

conceptualmente você pode imaginar que const std::string, por exemplo, é uma classe diferente do normal std::string, uma vez que a varianteconst está conceptualmente faltando as várias operações mutantes que estão disponíveis na variante não- const. Por exemplo, você pode imaginar conceptualmente que um const std::string simplesmente não tem um operador de atribuição+= ou qualquer outra operação mutante.Devo tentar que as coisas estejam corretas “mais cedo “ou”mais tarde”?

no início muito, muito, muito.

back-patching const correctness results in a snowball effect: every const you add “over here” requires four moreto be added ” over there.”

Add const early and often.

o que significa” const X* p”?

significa p aponta para um objeto da classe X, mas p não pode ser usado para mudar que X objeto (naturalmente p poderia ser NULL).

leia-o da direita para a esquerda: “p é um ponteiro para um X que é constante.”

por exemplo, se a classe X tem uma função de membro const tal como inspect() const, pode-se dizerp->inspect(). Mas se a classe X tem uma função não – const membro chamada mutate(), é anerror se você diz p->mutate().Significativamente, este erro é pego pelo compilador em tempo de compilação-nenhum teste de tempo de execução é feito. Isso significa que constnão abranda o seu programa e não requer que você escreva casos de teste extra para verificar as coisas em tempo de execução — o compilador faz o trabalho em tempo de compilação.

What’s the difference between “const X* p”, “X* const p” and “const X* const p”?

leia as declarações de ponteiro da direita para a esquerda.

  • const X* p significa ” p aponta para um X que é const“: o objeto X não pode ser alterado viap.
  • X* const p significa ” p é um const ponto para um X que não é const“: você não pode mudar o ponteiro pem si, mas você pode mudar o objeto X via p.
  • const X* const p significa ” p é um const ponteiro para um X que é const“: você não pode mudar o ponteiro pem si, nem pode mudar o objeto X via p.E, oh sim, eu mencionei para ler as suas declarações de ponteiro da direita para a esquerda?

    o que significa” const X& x”?

    significa x pseudónimos de objeto X, mas você não pode mudar isso X objeto via x.

    leia-o da direita para a esquerda: “x é uma referência a um X que é const.”

    por exemplo, se a classe X tem uma função de membro const tal como inspect() const, pode-se dizerx.inspect(). Mas se a classe X tem uma função não – const membro chamado mutate(), é um erro você diz x.mutate().

    Isso é totalmente simétrico, com ponteiros para const, incluindo o fato de que o compilador faz toda a verificação em tempo de compilação, o que significa const não abrandar o seu programa e não requer que você escreva teste extra-casos para verificar as coisas em tempo de execução.

    What do “X const& x” and “X const * p” mean?

    X const& x é equivalente a const X& x, e X const* x é equivalente aconst X* x.

    algumas pessoas preferem o estilo const – on-the-right, chamando-o de “consistente const “ou, usando um termo cunhado por Simon Brand,” East const. Na verdade, o estilo “Leste const” pode ser mais consistente do que a alternativa: o estilo “Leste const” alwaysputs o const à direita do que consente, enquanto o outro estilo às vezes coloca o const à esquerda e às vezes à direita (para declarações de ponteiros e funções de membro const).

    com o estilo ” Leste const“, uma variável local que é const é definida com o const à direita:int const a = 42;. Similarmente uma variável static que é const é definida como static double const x = 3.14;.Basicamente cada const termina à direita da coisa que consente, incluindo o const que é necessário para estar à direita: const declarações de ponteiro e com uma função const membro.

    o estilo” Leste const ” também é menos confuso quando usado com pseudónimos tipo: Por que foo e bar têm diferentes tipos aqui?

    using X_ptr = X*;const X_ptr foo;const X* bar;

    usando o estilo” este const ” torna isto mais claro:

    using X_ptr = X*;X_ptr const foo;X* const foobar;X const* bar;

    Ele é mais claro aqui que foo e foobar são do mesmo tipo e que bar é um tipo diferente.

    o estilo” Leste const ” também é mais consistente com as declarações de ponteiro. Contrastar o estilo tradicional:

    const X** foo;const X* const* bar;const X* const* const baz;

    com o estilo” East “const

    X const** foo;X const* const* bar;X const* const* const baz;

    apesar destes benefícios, o estilo “East” const-on-the-right ainda não é popular, então o código legado tende a ter o estilo tradicional.”X” & const x ” faz algum sentido?Não, é um disparate.

    para saber o que significa a declaração acima, leia-a da direita para a esquerda: “x é uma referência a X“. Mas isso é redundante-as referências são sempre const, no sentido de que você nunca pode reaquecer areference para fazê-lo se referir a um objeto diferente. Nunca. Com ou sem const.

    por outras palavras, “X& const x “é funcionalmente equivalente a” X& x“. Uma vez que você não está ganhando nada adicionando oconst após o &, você não deve adicioná — lo: ele vai confundir as pessoas-o const vai fazer algumas pessoas pensar que o X é const, como se você tivesse dito “const X& x“.

    What is a”const member function”?

    uma função membro que inspecciona (em vez de sofrer mutações) o seu objecto.

    a função de membro const é indicada por um sufixo const logo após a lista de parâmetros da função de membro. As funções dos membros com um sufixo const são chamadas “const funções dos Membros ” ou “inspectores”.”Funções membros sem um sufixoconst são chamadas” funções não – const membros “ou” mutantes.”

    class Fred {public: void inspect() const; // This member promises NOT to change *this void mutate(); // This member function might change *this};void userCode(Fred& changeable, const Fred& unchangeable){ changeable.inspect(); // Okay: doesn't change a changeable object changeable.mutate(); // Okay: changes a changeable object unchangeable.inspect(); // Okay: doesn't change an unchangeable object unchangeable.mutate(); // ERROR: attempt to change unchangeable object}

    a tentativa de chamar unchangeable.mutate() é um erro pego no tempo de compilação. Não há espaço de tempo de execução ou speedpenalty para const, e você não precisa escrever casos de teste para verificá-lo no tempo de execução.

    deve utilizar-se a função de membro que segue const em inspect(), para que o método não altere o estado do objecto (visível pelo cliente). Isso é ligeiramente diferente de dizer que o método não vai mudar os “bits brutos” do objeto struct. Compiladores de C++ não são autorizados a tomar a interpretação “bitwise” a menos que eles possam resolver o problema do processo dealiasing, que normalmente não pode ser resolvido (ou seja, um não-const alias poderia existir que poderia modificar o estado do objeto). Outra (importante) visão desta questão de alusão: apontar para um objeto com um ponteiro-para-constnão garante que o objeto não vai mudar; ele apenas promete que o objeto não vai mudar através desse ponteiro.

    Qual é a relação entre uma função return-by-reference e uma função de membro const?

    se quiser devolver um membro do seu objecto this por referência de um método de inspector, deve devolvê-lo utilizando o reference-to-const (const X& inspect() const) ou por valor (X inspect() const).

    class Person {public: const std::string& name_good() const; // Right: the caller can't change the Person's name std::string& name_evil() const; // Wrong: the caller can change the Person's name int age() const; // Also right: the caller can't change the Person's age // ...};void myCode(const Person& p) // myCode() promises not to change the Person object...{ p.name_evil() = "Igor"; // But myCode() changed it anyway!!}

    a boa notícia é que o compilador irá frequentemente apanhá-lo se se enganar. Em particular, se você acidentalmente reverter um membro do seu objeto this por referência nãoconst, como em Person::name_evil() acima, o compilador irá frequentemente detectá-lo e dar-lhe um erro de tempo de compilação ao compilar as entranhas de, Neste caso,Person::name_evil().

    a má notícia é que o compilador nem sempre o pegará: há alguns casos em que o compilador simplesmente não lhe dará uma mensagem de erro de tempo de compilação.Tradução: você precisa pensar. Se isso o assusta, encontre outra linha de trabalho; “pensar” não é uma palavra de quatro letras.

    lembre-se da “filosofia” espalhada ao longo desta secção: uma função de Membro não deve mudar (ou permitir que um ouvinte mude) o estado lógico do objectothis (também conhecido como estado abstracto ou estado significante). Pense no que um objeto significa, não como ele é implementado internamente. A idade e o nome de uma pessoa são partes lógicas da pessoa, mas o vizinho e empregador da pessoa não são. Um método de inspetor que retorna parte do estado lógico / abstrato / sentido do objeto thisnão deve retornar um não-const ponteiro (ou referência) para essa parte,independente de se essa parte é internamente implementada como um membro de dados direto fisicamente embutido dentro do objetothis ou de alguma outra forma.

    Qual é o problema com “const-overloading”?

    const sobrecarga ajuda a atingir const correcção.

    const sobrecarga é quando se tem um método de inspecção e um método de mutação com o mesmo nome e o mesmo número e tipos de parâmetros. Os dois métodos distintos diferem apenas em que o espectador é const e o mutador não éconst.

    o uso mais comum de const sobrecarga é com o operador subescrito. Você deve geralmente tentar usar um dos modelos de contêineres do thestandard, como std::vector, mas se você precisar criar sua própria classe que tem um subscriptoperador, aqui está a regra geral: os operadores subscript muitas vezes vêm em pares.

    class Fred { /*...*/ };class MyFredList {public: const Fred& operator (unsigned index) const; // Subscript operators often come in pairs Fred& operator (unsigned index); // Subscript operators often come in pairs // ...};

    o operador const subscript devolve uma const-referência, de modo que o compilador impedirá os utilizadores de cortarem inadvertidamente/alterarem o Fred. O operador não – const subscript devolve uma referência não – const, que é a sua forma de rotular os seus chamadores (e o compilador) que os seus chamadores podem modificar o objecto Fred.

    When a user of your MyFredList class calls the subscript operator, the compiler selects which overload to call basedon the constness of their MyFredList. Se o chamador tiver um MyFredList a ou MyFredList& a, então a vai callthe nãoconst subscrito operador, e o chamador irá acabar com um não-const referência a uma Fred:

    Por exemplo, suponha class Fred tem um inspector-método inspect() const e um mutator method mutate():

    void f(MyFredList& a) // The MyFredList is non-const{ // Okay to call methods that inspect (look but not mutate/change) the Fred at a: Fred x = a; // Doesn't change to the Fred at a: merely makes a copy of that Fred a.inspect(); // Doesn't change to the Fred at a: inspect() const is an inspector-method // Okay to call methods that DO change the Fred at a: Fred y; a = y; // Changes the Fred at a a.mutate(); // Changes the Fred at a: mutate() is a mutator-method}

    no Entanto, se o chamador tiver um const MyFredList a ou const MyFredList& a, então a vai chamar a const subscriptoperator, e o chamador irá terminar com um const referência a uma Fred. Isso permite que o chamador para inspecionar o Fredem a, mas impede que o chamador, inadvertidamente, transformando-a/alterar o Fred em a.

    void f(const MyFredList& a) // The MyFredList is const{ // Okay to call methods that DON'T change the Fred at a: Fred x = a; a.inspect(); // Compile-time error (fortunately!) if you try to mutate/change the Fred at a: Fred y; a = y; // Fortunately(!) the compiler catches this error at compile-time a.mutate(); // Fortunately(!) the compiler catches this error at compile-time}

    cont overloading for subscript-and funcall-operators is illustrated here, here, here, and here.

    você pode, naturalmente, também usar const – sobrecarga para outras coisas que não o operador subscript.

    como pode ajudar-me a conceber melhores classes se eu distinguir o estado lógico do estado físico?

    porque isso o incentiva a projetar suas aulas de fora para dentro e não de dentro para fora, o que, por sua vez, torna suas classes e objetos mais fáceis de entender e usar, mais intuitivos, menos propensos a erros, e mais rápido. Isso é uma simplificação excessiva. Para entender todos os “se”, ” se ” e “mas”, terá de ler o resto da resposta!)

    vamos entender isso de dentro para fora-você (deve) projetar suas aulas do lado de fora, mas se você é novo para este conceito, é mais fácil de entender a partir do lado de fora.

    no interior, os seus objectos têm estado físico (ou concreto ou pontiagudo). Este é o estado que é fácil para os programadores ver e entender; é o estado que estaria lá se a classe fosse apenas um estilo C struct.

    no exterior, os seus objectos têm utilizadores da sua classe, e estes utilizadores estão limitados a utilizar apenas public funções de membros e friendS. Estes usuários externos também percebem o objeto como tendo estado, por exemplo, se o objeto é da classe Rectangle com métodos width(), height() e area(), seus usuários diriam que esses três são todos parte do estado lógico (ou abstrato ou sentido) do objeto. Para um utilizador externo, o Rectangle objectactualmente tem uma área, mesmo que essa área seja calculada em tempo real (por exemplo, se o método area() devolve o produto da largura e altura do objecto). Na verdade, e este é o ponto importante, seus usuários não sabem e não se importam como você implementa qualquer um destes métodos; seus usuários ainda percebem, a partir de sua perspectiva, que seu objeto logicamente tem um estado ameaningwise de largura, altura e área.

    o exemplo area() mostra um caso em que o estado lógico pode conter elementos que não são diretamente realizados no estado físico. O oposto também é verdadeiro: classes às vezes intencionalmente esconder parte do estado físico(Concreto, bitwise) de seus objetos dos usuários — eles intencionalmente não fornecem quaisquer funções public membros oufriends que permitiriam aos usuários ler ou escrever ou até mesmo saber sobre este estado oculto. Isso significa que há aberturas no estado físico do objeto que não têm elementos correspondentes no estado lógico do objeto.

    como exemplo deste último caso, um objecto de colecção pode guardar a sua última pesquisa na esperança de melhorar o desempenho da sua próxima pesquisa. Este cache é certamente parte do estado físico do objeto, mas lá está um detalhe de implementação interna que provavelmente não será exposto aos usuários — provavelmente não será parte do estado slógico do objeto. Dizer o que é fácil se você pensa de fora para dentro: se os usuários do collection-object têm noway para verificar o estado do cache em si, então o cache é transparente, e não faz parte do estado logical do objeto.

    a constância das minhas funções de membro público deve basear-se no que o método faz ao estado lógico do objecto, ou ao estado físico?

    lógico.Não há maneira de facilitar a próxima parte. Vai doer. A melhor recomendação é sentar-se. E por favor, para vossa segurança, certifiquem-se de que não há instrumentos afiados por perto.

    vamos voltar para o exemplo Coleção-objeto. Lembrar: há um método de pesquisa que analisa a última pesquisa na esperança de acelerar futuras pesquisas.

    vamos afirmar o que é provavelmente óbvio: assumir que o método de pesquisa não faz alterações a qualquer estado lógico do objeto de coleta.Então … chegou a hora de te magoar. Estás pronto?

    aqui vem: se o método de pesquisa não faz nenhuma alteração a qualquer estado lógico do objeto de coleção, mas ele muda o estado físico do objeto de coleção (ele faz uma mudança muito real para o cache muito real), o método de thelookup deve ser const?A resposta é um sim retumbante. (Há exceções para cada regra, então “sim” deve realmente ter um asterisco ao lado dele,mas a grande maioria das vezes, a resposta é sim.)

    isto é tudo sobre “lógico const “sobre” físico const.”Significa que a decisão sobre se decorar amethod com const deve depender principalmente se esse método deixa o estado lógico inalterado, independentemente(você está sentado?) (você pode querer sentar-se) independentemente de o método acontece para fazer mudanças muito reais para o estado físico muito real do objeto.

    No caso de que não afundar ou, no caso de você ainda não estão na dor, vamos provocá-lo separadamente em dois casos:

    • Se um método de alterações em qualquer parte do objeto da lógica do estado, que logicamente é um modificador; ele não deve ser const evenif (como realmente acontece!) o método não altera nenhum pedaço físico do estado concreto do objeto.Por outro lado, um método é logicamente um inspetor e deve ser const se ele nunca muda qualquer parte do estado slógico do objeto, mesmo se (como realmente acontece!) the method changes physical bits of the object’s concrete state.Se estiver confuso, leia novamente.

      se você não está confuso, mas está irritado, bom: você pode não gostar ainda, mas pelo menos você entende. Faça uma repetição de respiração profunda depois de mim: “a ness constde um método deve fazer sentido de fora do objeto.”

      se você ainda está com raiva, repita isso três vezes: “a constância de um método deve fazer sentido para os usuários do objeto, e esses usuários podem ver apenas o estado lógico do objeto.”

      se você ainda está com raiva, desculpe, é o que é. Aguenta-te e vive com isso. Sim, haverá excepções.; todas as regras eram elas. Mas como regra, em geral, esta noção lógica const é boa para você e boa para o seu software.Mais uma coisa. Isto vai ficar iNano, mas vamos ser precisos sobre se um método muda o estado logical do objeto. Se você estiver fora da classe — você é um usuário normal, cada experiência que você poderia realizar (cada método ou Consequência de métodos que você chama) teria os mesmos resultados (mesmos valores de retorno, as mesmas exceções ou falta de exceções), independentemente de ter chamado pela primeira vez esse método de pesquisa. Se a função de pesquisa mudou qualquer comportamento futuro de qualquer método futuro (não apenas tornando-o mais rápido, mas mudou o resultado, mudou o valor de retorno, mudou a percepção), então o método de pesquisa mudou o estado lógico do objeto — é um mutuante. Mas se o método de pesquisa mudou nada além de talvez fazer algumas coisas mais rápido, então é um inspetor.

      What do I do if I want a const member function to make an “invisible” change to a data member?

      utilizar mutable (ou, em último recurso, utilizar const_cast).

      uma pequena percentagem de inspetores precisa fazer mudanças no estado físico de um objeto que não podem ser observadas por externalusers — mudanças no estado físico, mas não lógico.

      por exemplo, o objecto da colecção discutido anteriormente permitiu a sua última pesquisa na esperança de melhorar o desempenho da sua próxima pesquisa. Uma vez que o cache, neste exemplo, não pode ser observado diretamente por qualquer parte da interface pública do objeto collection (além de timing), sua existência e estado não é parte do estado lógico do objeto, então as mudanças são invisíveis para os usuários externos. O método de pesquisa é um inspetor, uma vez que nunca muda o estado lógico do objeto, independentemente do fato de que, pelo menos para a implementação atual, ele altera o estado físico do objeto.Quando os métodos alteram o estado físico mas não lógico, o método deve geralmente ser marcado como const, uma vez que é realmente um método de inspecção. Isso cria um problema: quando o compilador vê o seu método const mudando o estado físico do objeto this, ele se queixará-ele dará ao seu código uma mensagem de erro.

      a linguagem de compilador C++ usa a palavra-chave mutable para ajudá-lo a abraçar esta noção lógica const. Neste caso, você marcaria o cache com a palavra-chave mutable, dessa forma o compilador sabe que é permitido mudar dentro de um métodoconst ou através de qualquer outro const ponteiro ou referência. Na nossa linguagem, a palavra-chave mutable marca as porções do estado físico do objecto que não fazem parte do estado lógico.

      a palavra-chave mutable vai imediatamente antes da declaração do membro de dados, ou seja, o mesmo lugar onde você poderia colocarconst. A outra abordagem, não preferido, é lançar fora a const‘ness do this ponteiro, provavelmente através deconst_cast palavras-chave:

      Set* self = const_cast<Set*>(this); // See the NOTE below before doing this!

      Após essa linha, self vai ter o mesmo bits como this, isto é, self == this, mas self é um Set* em vez de umconst Set* (tecnicamente this é um const Set* const, mas o mais à direita const é irrelevante para esta discussão).Isso significa que você pode usar self para modificar o objeto apontado por this.Nota: existe um erro extremamente improvável que pode ocorrer com const_cast. Só acontece quando três rarefeitos são combinados ao mesmo tempo.: um membro de dados que deve ser mutable (como discutido acima), um compilerthat não oferece suporte a mutable palavras-chave e/ou um programador que não usá-lo, e um objeto que foi originallydefined ser const (em oposição a uma normal, nãoconst objeto que é apontado por um ponteiro-para-const).Embora esta combinação seja tão rara que pode nunca acontecer com você, se alguma vez acontecer, o código pode não funcionar (theStandard diz que o comportamento é indefinido).Se alguma vez quiser utilizar const_cast, utilize mutable. Em outras palavras, se você alguma vez precisar mudar um membro de um objeto, e esse objeto é apontado por um ponteiro-para-const, a coisa mais segura e mais simples a fazer é adicionar mutable à declaração do membro. Você pode usar const_cast se você tem certeza de que o objeto real não é const (por exemplo, se estiver certeza de que o objeto é declarado algo parecido com isto: Set s;), mas se o próprio objeto pode ser const (por exemplo, se pode ser declarado como: const Set s;), use mutable em vez de const_cast.

      por favor, não escreva dizendo que a versão X do compilador Y na máquina Z permite alterar um não-mutable membro de um objectoconst. Eu não me importo — é ilegal de acordo com a linguagem e seu código provavelmente vai falhar em um compilador diferente ou mesmo uma versão diferente (uma atualização) do mesmo compilador. Diz que não. Utilizar mutable em vez disso. Escrever codethat é garantido para funcionar, não um código que não parece quebrar.Const_cast significa oportunidades de otimização perdidas?

      em teoria, sim; na prática, não.Mesmo que a linguagem proscrevesse const_cast, a única maneira de evitar a descarga do cache de registo através de uma chamada de função dos Membros seria resolver o problema do aliasing (i.e., para provar que não há não-const ponteiros que apontam para o objeto). Isto só pode acontecer em raros casos (quando o objeto é construído no âmbito do const memberfunction invocação, e quando os não-const função de membro invocações entre o objeto e a construçãoconst função de membro invocação de são estaticamente ligado, e quando cada uma destas invocações, também é inlined, andwhen o construtor de si é inlined, e quando todas as funções de membro do construtor chama são inline).

      por que o compilador me permite mudar um int depois de eu ter apontado para ele com um const int*?

      porque “const int* p “significa” p promete não mudar o *p, “não” *p promete não mudar.”

      fazendo com que um const int* aponte para um int não const-ify o int. O int não pode ser alterado através deconst int*, mas se alguém tiver um int* (nota: não const) que aponta a (“aliases”), o mesmo int, entãoint* pode ser usado para alterar o int. Por exemplo:

      void f(const int* p1, int* p2){ int i = *p1; // Get the (original) value of *p1 *p2 = 7; // If p1 == p2, this will also change *p1 int j = *p1; // Get the (possibly new) value of *p1 if (i != j) { std::cout << "*p1 changed, but it didn't change via pointer p1!\n"; assert(p1 == p2); // This is the only way *p1 could be different }}int main(){ int x = 5; f(&x, &x); // This is perfectly legal (and even moral!) // ...}

      Note que main() e f(const int*,int*) podem estar em diferentes unidades de compilação compiladas em diferentes dias da semana. Nesse caso, não há nenhuma maneira do compilador pode possivelmente detectar a aliasing no tempo de compilação. Por conseguinte, não é possível estabelecer uma regra linguística que proíba este tipo de coisas. Na verdade, nós nem queremos fazer tal arule, já que em geral é considerado um recurso que você pode ter muitos ponteiros apontando para a mesma coisa. O fato de que um desses ponteiros promete não mudar a “coisa” subjacente é apenas uma promessa feita pelo ponteiro; não é uma promessa feita pela “coisa”.”Const Fred * p” significa que * p não pode mudar?Não! (This is related to the FAQ about aliasing of int pointers.)

      const Fred* p” significa que o Fred não pode ser alterado através do ponteiro p, mas pode haver outras maneiras de obter em theobject sem passar através de um const (como um alias nãoconst ponteiro como um Fred*). Por exemplo, se você tem dois ponteiros “const Fred* p “e” Fred* q ” que apontam para o mesmo objeto Fred (aliasing), pointer q pode ser usado para alterar o objeto Fred mas pointer p não pode.

      class Fred {public: void inspect() const; // A const member function void mutate(); // A non-const member function};int main(){ Fred f; const Fred* p = &f; Fred* q = &f; p->inspect(); // Okay: No change to *p p->mutate(); // Error: Can't change *p via p q->inspect(); // Okay: q is allowed to inspect the object q->mutate(); // Okay: q is allowed to mutate the object f.inspect(); // Okay: f is allowed to inspect the object f.mutate(); // Okay: f is allowed to mutate the object // ...}

      Why am I getting an error converting a Foo * * → const Foo**?

      porque converter Foo**const Foo** seria inválido e perigoso.

      C++ permite a conversão (segura) Foo*Foo const*, mas dá um erro se você tentar implicitamente converter Foo**const Foo**.

      a razão pela qual esse erro é uma coisa boa é dada abaixo. Mas primeiro, aqui está a solução mais comum: simplychange const Foo** a const Foo* const*:

      class Foo { /* ... */ };void f(const Foo** p);void g(const Foo* const* p);int main(){ Foo** p = /*...*/; // ... f(p); // ERROR: it's illegal and immoral to convert Foo** to const Foo** g(p); // Okay: it's legal and moral to convert Foo** to const Foo* const* // ...}

      A razão, a conversão do Foo**const Foo** é perigoso é que ele iria deixá-lo silenciosamente e accidentallymodify um const Foo objeto sem um elenco:

      class Foo {public: void modify(); // make some modification to the this object};int main(){ const Foo x; Foo* p; const Foo** q = &p; // q now points to p; this is (fortunately!) an error *q = &x; // p now points to x p->modify(); // Ouch: modifies a const Foo!! // ...}

      Se a q = &p linha eram legais, q seria apontando em p. A linha seguinte, *q = &x, muda p em si (desde *q é p) para apontar x. Isso seria uma coisa ruim, uma vez que teríamos perdido o qualificador const: p é um Foo* masx é um const Foo. A linha p->modify() explora a capacidade de modificar seu referent, que é o problema real, uma vez que acabamos modificando um const Foo.Por analogia, se você esconder um criminoso sob um disfarce legal, ele pode então explorar a confiança dada a esse disfarce.Isso é mau.

      felizmente, o C++ impede-o de fazer isto: a linha q = &p é assinalada pelo compilador C++ como um erro de compilação. Chamada de Atenção: Por favor, não indique o seu modo de contornar essa mensagem de erro de tempo de compilação. Diz Que Não!

      (Nota: existe uma semelhança conceptual entre esta e a proibição de converter Derived** paraBase**.)

Deixe uma resposta

O seu endereço de email não será publicado.