Significado de 'const' por último em uma declaração de função de uma classe?

727

Qual é o significado de constdeclarações como essas? A constme confunde.

class foobar
{
  public:
     operator int () const;
     const char* foo() const;
};
Rakete1111
fonte

Respostas:

951

Quando você adiciona a constpalavra-chave a um método, o thisponteiro se torna essencialmente um ponteiro para um constobjeto e, portanto, não é possível alterar nenhum dado de membro. (A menos que você use mutable, mais sobre isso mais tarde).

A constpalavra-chave faz parte da assinatura das funções, o que significa que você pode implementar dois métodos semelhantes, um chamado quando o objeto é conste outro que não é.

#include <iostream>

class MyClass
{
private:
    int counter;
public:
    void Foo()
    { 
        std::cout << "Foo" << std::endl;    
    }

    void Foo() const
    {
        std::cout << "Foo const" << std::endl;
    }

};

int main()
{
    MyClass cc;
    const MyClass& ccc = cc;
    cc.Foo();
    ccc.Foo();
}

Isso produzirá

Foo
Foo const

No método non-const, você pode alterar os membros da instância, o que não é possível na constversão. Se você alterar a declaração do método no exemplo acima para o código abaixo, você receberá alguns erros.

    void Foo()
    {
        counter++; //this works
        std::cout << "Foo" << std::endl;    
    }

    void Foo() const
    {
        counter++; //this will not compile
        std::cout << "Foo const" << std::endl;
    }

Isso não é completamente verdade, porque você pode marcar um membro como mutablee um constmétodo pode alterá-lo. É usado principalmente para contadores internos e outras coisas. A solução para isso seria o código abaixo.

#include <iostream>

class MyClass
{
private:
    mutable int counter;
public:

    MyClass() : counter(0) {}

    void Foo()
    {
        counter++;
        std::cout << "Foo" << std::endl;    
    }

    void Foo() const
    {
        counter++;    // This works because counter is `mutable`
        std::cout << "Foo const" << std::endl;
    }

    int GetInvocations() const
    {
        return counter;
    }
};

int main(void)
{
    MyClass cc;
    const MyClass& ccc = cc;
    cc.Foo();
    ccc.Foo();
    std::cout << "Foo has been invoked " << ccc.GetInvocations() << " times" << std::endl;
}

o que produziria

Foo
Foo const
Foo has been invoked 2 times
Mats Fredriksson
fonte
187

A const significa que o método promete não alterar nenhum membro da classe. Você seria capaz de executar os membros do objeto que estão tão marcados, mesmo se o próprio objeto estiver marcado const:

const foobar fb;
fb.foo();

seria legal.

Veja Quantos e quais são os usos de "const" em C ++? Para maiores informações.

Blair Conrad
fonte
47

O constqualificador significa que os métodos podem ser chamados com qualquer valor de foobar. A diferença ocorre quando você considera chamar um método não-const em um objeto const. Considere se seu foobartipo possui a seguinte declaração de método extra:

class foobar {
  ...
  const char* bar();
}

O método bar()é não const e só pode ser acessado a partir de valores não const.

void func1(const foobar& fb1, foobar& fb2) {
  const char* v1 = fb1.bar();  // won't compile
  const char* v2 = fb2.bar();  // works
}

A idéia por trás disso consté marcar métodos que não alterem o estado interno da classe. Esse é um conceito poderoso, mas não é realmente aplicável em C ++. É mais uma promessa do que uma garantia. E um que é frequentemente quebrado e facilmente quebrado.

foobar& fbNonConst = const_cast<foobar&>(fb1);
JaredPar
fonte
3
Eu pensei que a resposta é sobre outros métodos const e não sobre objetos const.
Mykola Golubyev
Obrigado pela "A idéia por trás disso consté marcar métodos que não alteram o estado interno da classe". Era realmente o que eu estava procurando.
Kovac 17/03/19
1
@JaredPar significa que qualquer função de membro representando uma operação somente leitura deve ser marcada como const?
Kovac 17/03/19
26

Estes const significam que o compilador irá Erro se o método 'with const' alterar dados internos.

class A
{
public:
    A():member_()
    {
    }

    int hashGetter() const
    {
        state_ = 1;
        return member_;
    }
    int goodGetter() const
    {
        return member_;
    }
    int getter() const
    {
        //member_ = 2; // error
        return member_;
    }
    int badGetter()
    {
        return member_;
    }
private:
    mutable int state_;
    int member_;
};

O teste

int main()
{
    const A a1;
    a1.badGetter(); // doesn't work
    a1.goodGetter(); // works
    a1.hashGetter(); // works

    A a2;
    a2.badGetter(); // works
    a2.goodGetter(); // works
    a2.hashGetter(); // works
}

Leia isto para mais informações

Mykola Golubyev
fonte
1
Uma pergunta sobre constfunções-membro que não menciona mutável é incompleta, na melhor das hipóteses.
IInspectable
13

A resposta de Blair está na marca.

No entanto, observe que há um mutablequalificador que pode ser adicionado aos membros de dados de uma classe. Qualquer membro marcado pode ser modificado em um constmétodo sem violar o constcontrato.

Você pode usar isso (por exemplo) se desejar que um objeto se lembre de quantas vezes um método específico é chamado, sem afetar a constância "lógica" desse método.

Alnitak
fonte
10

Significado de uma função de membro Const no C ++ Common Knowledge: A programação intermediária essencial fornece uma explicação clara:

O tipo do ponteiro this em uma função de membro não const de uma classe X é X * const. Ou seja, é um ponteiro constante para um X não constante (consulte Const Pointers e Pointers to Const [7, 21]). Como o objeto ao qual isso se refere não é const, ele pode ser modificado. O tipo disso em uma função de membro const de uma classe X é const X * const. Ou seja, é um ponteiro constante para um X constante. Como o objeto ao qual isso se refere é const, ele não pode ser modificado. Essa é a diferença entre as funções de membro const e não-const.

Então, no seu código:

class foobar
{
  public:
     operator int () const;
     const char* foo() const;
};

Você pode pensar assim:

class foobar
{
  public:
     operator int (const foobar * const this) const;
     const char* foo(const foobar * const this) const;
};
Nan Xiao
fonte
thisnão é const. A razão pela qual não pode ser modificado é que é um valor inicial.
Brian
7

Quando você usa consta assinatura do método (como você disse const char* foo() const;:), está dizendo ao compilador que a memória apontada por thisnão pode ser alterada por esse método (que está fooaqui).

Matrix Buster
fonte
6

Eu gostaria de acrescentar o seguinte ponto.

Você também pode fazer -lhe um const &econst &&

Assim,

struct s{
    void val1() const {
     // *this is const here. Hence this function cannot modify any member of *this
    }
    void val2() const & {
    // *this is const& here
    }
    void val3() const && {
    // The object calling this function should be const rvalue only.
    }
    void val4() && {
    // The object calling this function should be rvalue reference only.
    }

};

int main(){
  s a;
  a.val1(); //okay
  a.val2(); //okay
  // a.val3() not okay, a is not rvalue will be okay if called like
  std::move(a).val3(); // okay, move makes it a rvalue
}

Sinta-se livre para melhorar a resposta. Eu não sou especialista

coder3101
fonte
1
*thisé sempre um lvalue, mesmo se a função de membro for qualificada para rvalue-ref e for chamada em um rvalue. Exemplo .
HolyBlackCat 26/03/19
1
Sim, então como devo melhorar minha resposta atual?
Codificador3101 26/03/19
Quero dizer o que escrever em comentário no bloco, que justifica o comportamento
coder3101
Atualizada. Tudo bem?
Codec3101 26/03/19
2

A palavra-chave const usada com a declaração da função especifica que é uma função de membro const e não poderá alterar os membros de dados do objeto.

Chandra Shekhar
fonte
1

https://isocpp.org/wiki/faq/const-correctness#const-member-fns

O que é uma " constfunção de membro"?

Uma função de membro que inspeciona (em vez de modifica) seu objeto.

Uma constfunção de membro é indicada por um constsufixo logo após a lista de parâmetros da função de membro. As funções de membro com um constsufixo são chamadas de "funções de membro const" ou "inspetores". As funções de membro sem constsufixo são chamadas de "funções de membro não const" ou "mutadores".

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 chamada unchangeable.mutate()é um erro detectado em tempo de compilação. Não há espaço para o tempo de execução ou penalidade de velocidade const, e você não precisa escrever casos de teste para verificá-lo no tempo de execução.

A função de membro constà direita inspect()deve ser usada para significar que o método não alterará o estado abstrato do objeto (visível ao cliente). Isso é um pouco diferente de dizer que o método não altera os “bits brutos” da estrutura do objeto. Os compiladores C ++ não podem usar a interpretação “bit a bit”, a menos que possam resolver o problema de aliasing, que normalmente não pode ser resolvido (ou seja, poderia existir um alias não const que pudesse modificar o estado do objeto). Outro insight (importante) desse problema de alias: apontar para um objeto com um ponteiro para const não garante que o objeto não seja alterado; apenas promete que o objeto não será alterado por esse ponteiro .

qwr
fonte