¿Cuál es el significado de ‘const’ al final de la declaración de una función miembro?

7 minutos de lectura

Cuál es el significado de const en declaraciones como estas?

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

Avatar de usuario de Mats Fredriksson
Esteras Fredriksson

Cuando agregas el const palabra clave a un método el this puntero se convertirá esencialmente en un puntero a const objeto y, por lo tanto, no puede cambiar ningún dato de miembro. (A menos que utilice mutablemás sobre eso más adelante).

El const La palabra clave es parte de la firma de funciones, lo que significa que puede implementar dos métodos similares, uno que se llama cuando el objeto es consty uno que no lo es.

#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();
}

Esto generará

Foo
Foo const

En el método non-const puede cambiar los miembros de la instancia, lo que no puede hacer en el const versión. Si cambia la declaración del método en el ejemplo anterior al código a continuación, obtendrá algunos errores.

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

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

Esto no es completamente cierto, porque puedes marcar a un miembro como mutable y un const El método puede cambiarlo. Se usa principalmente para contadores internos y esas cosas. La solución para eso sería el siguiente código.

#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;
}

que daría salida

Foo
Foo const
Foo has been invoked 2 times

  • ¿Qué pasa si solo hago un método const pero sin un método normal, y luego llamo al método usando un objeto que no es const? Mi código generalmente funciona bien. ¿Es malo o dañino o algo así?

    – KhiemGOM

    27 de agosto de 2021 a las 1:55

  • @KhiemGOM Eso está completamente bien, y es un patrón bastante normal para miembros de solo lectura.

    –Mats Fredriksson

    15 de noviembre de 2021 a las 19:11

  • mutable y const tiene sentido para mi. Pero lo que no entiendo bien es que si un const A la función miembro le gustaría cambiar el valor de un miembro, ¿por qué no descartarlo? const y convertirse en una función de miembro no const? ¿Tiene algo que ver con la velocidad? O es como dadas dos variables miembro, una tiene un mutable palabra clave y otra no, en un const función miembro, nos gustaría cambiar solo el valor de la variable con mutable palabra clave pero quiere mantener otra intacta?

    – León Lai

    21 de febrero a las 11:21


Avatar de usuario de Blair Conrad
Blair Conrado

La const significa que el método promete no alterar ningún miembro de la clase. Podría ejecutar los miembros del objeto que están marcados, incluso si el objeto mismo estuviera marcado const:

const foobar fb;
fb.foo();

sería legal.

Ver ¿Cuántos y cuáles son los usos de “const” en C++? para más información.

Avatar de usuario de JaredPar
jaredpar

El const calificador significa que los métodos pueden ser llamados en cualquier valor de foobar. La diferencia surge cuando considera llamar a un método no constante en un objeto constante. Considere si su foobar type tenía la siguiente declaración de método adicional:

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

El método bar() no es constante y solo se puede acceder desde valores no constantes.

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

La idea detrás const aunque es para marcar métodos que no alterarán el estado interno de la clase. Este es un concepto poderoso, pero en realidad no se aplica en C++. Es más una promesa que una garantía. Y uno que a menudo se rompe y se rompe fácilmente.

foobar& fbNonConst = const_cast<foobar&>(fb1);

  • Pensé que la respuesta es sobre otros métodos const y no sobre objetos const.

    – Mykola Golubiev

    15 de abril de 2009 a las 14:14

  • Gracias por “La idea detrás const aunque es para marcar métodos que no alterarán el estado interno de la clase”. Eso es realmente lo que estaba buscando.

    – kovac

    17 de marzo de 2019 a las 9:01

  • @JaredPar, ¿significa esto que cualquier función miembro que represente una operación de solo lectura debe marcarse como const?

    – kovac

    17 de marzo de 2019 a las 9:03

Avatar de usuario de Mykola Golubyev
Mykola Golubiev

Estas const significan que el compilador generará un error si el método ‘con const’ cambia los datos 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_;
};

La prueba

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
}

Leer este para más información

La respuesta de Blair está en la marca.

Sin embargo tenga en cuenta que hay un mutable calificador que se puede agregar a los miembros de datos de una clase. Cualquier miembro así marcado poder modificarse en un const método sin violar el const contrato.

Es posible que desee usar esto (por ejemplo) si desea que un objeto recuerde cuántas veces se llama a un método en particular, sin afectar la constancia “lógica” de ese método.

Significado de una función miembro constante en C++ Common Knowledge: Essential Intermediate Programming da una explicación clara:

El tipo del puntero this en una función miembro no const de una clase X es X * const. Es decir, es un puntero constante a una X no constante (consulte Punteros constantes y Punteros a constantes). [7, 21]). Debido a que el objeto al que se refiere esto no es const, se puede modificar. El tipo de this en una función miembro const de una clase X es const X * const. Es decir, es un puntero constante a una constante X. Debido a que el objeto al que se refiere es const, no se puede modificar. Esa es la diferencia entre funciones miembro constantes y no constantes.

Así que en tu código:

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

Puedes pensarlo así:

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

Me gustaría añadir el siguiente punto.

También puedes hacer es un const & y const &&

Entonces,

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
}

Siéntete libre de mejorar la respuesta. no soy un experto

  • *this es siempre un lvalue, incluso si la función miembro es rvalue-ref-qualified y se llama en un rvalue. Ejemplo.

    – Santo Gato Negro

    26/03/2019 a las 17:30

  • Actualizado. ¿Está bien?

    – codificador3101

    26 de marzo de 2019 a las 18:05

¿Ha sido útil esta solución?