¿Qué significa 'const' último en una declaración de función de una clase?

727

¿Cuál es el significado de consten declaraciones como estas? El constme confunde.

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

Respuestas:

951

Cuando agrega la constpalabra clave a un método, el thispuntero se convertirá esencialmente en un puntero al constobjeto y, por lo tanto, no puede cambiar ningún dato de miembro. (A menos que use mutable, más sobre eso más adelante).

La constpalabra 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 otro que no.

#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 dará salida

Foo
Foo const

En el método no constante, puede cambiar los miembros de la instancia, lo que no puede hacer en la constversión. Si cambia la declaración del método en el ejemplo anterior al código siguiente, 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 puede marcar un miembro como mutabley un constmétodo puede cambiarlo. Se usa principalmente para contadores internos y otras 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
Mats Fredriksson
fuente
187

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

const foobar fb;
fb.foo();

Sería legal.

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

Blair Conrad
fuente
47

El constcalificador significa que los métodos se pueden invocar en cualquier valor de foobar. La diferencia se produce cuando considera llamar a un método no const en un objeto const. Considere si su foobartipo 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
}

Sin constembargo, la idea detrás es marcar métodos que no alteren el estado interno de la clase. Este es un concepto poderoso pero en realidad no es exigible 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);
JaredPar
fuente
3
Pensé que la respuesta es sobre otros métodos const y no sobre objetos const.
Mykola Golubyev el
Gracias por la "La idea subyacente constes marcar métodos que no alteren el estado interno de la clase". Eso es realmente lo que estaba buscando.
kovac
1
@JaredPar, ¿significa esto que cualquier función miembro que represente una operación de solo lectura debe marcarse como const?
kovac
26

Estos const significan que el compilador producirá 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
}

Lea esto para más información

Mykola Golubyev
fuente
1
Una pregunta sobre constfunciones de miembros que no menciona mutable es incompleta en el mejor de los casos.
Inspeccionable el
13

La respuesta de Blair está en el blanco.

Sin embargo, tenga en cuenta que hay un mutablecalificador que se puede agregar a los miembros de datos de una clase. Cualquier miembro así marcado puede modificarse en un constmétodo sin violar elconst contrato.

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

Alnitak
fuente
10

Significado de una función miembro de Const en C ++ Conocimiento común: la programación básica esencial 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 (ver Punteros Const y Punteros a Const [7, 21]). Como el objeto al que se refiere esto no es constante, puede modificarse. El tipo de esto en una función miembro const de una clase X es const X * const. Es decir, es un puntero constante a una X constante. Debido a que el objeto al que esto se refiere es constante, no se puede modificar. Esa es la diferencia entre las funciones miembro const y no const.

Entonces 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;
};
Nan Xiao
fuente
thisno es const. La razón por la que no se puede modificar es que es un valor privalente.
Brian
7

cuando usas constla firma del método (como dijiste:) const char* foo() const;le estás diciendo al compilador que la memoria apuntada por thisno puede cambiarse con este método (que está fooaquí).

Matrix Buster
fuente
6

Me gustaría agregar el siguiente punto.

También puedes convertirlo en un const &yconst &&

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éntase libre de mejorar la respuesta. No soy un experto

coder3101
fuente
1
*thissiempre es un lvalue, incluso si la función miembro es rvalue-ref-qualified y se llama en un rvalue. Ejemplo .
HolyBlackCat
1
Sí, entonces, ¿cómo debo mejorar mi respuesta actual?
coder3101
Me refiero a qué escribir en el comentario en el bloque, eso justifica el comportamiento
coder3101
Actualizado. Esta bien?
coder3101
2

La palabra clave const utilizada con la declaración de función especifica que es una función miembro const y no podrá cambiar los miembros de datos del objeto.

Chandra Shekhar
fuente
1

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

¿Qué es una " constfunción miembro"?

Una función miembro que inspecciona (en lugar de mutar) su objeto.

Una constfunción miembro se indica mediante un constsufijo justo después de la lista de parámetros de la función miembro. Las funciones miembro con constsufijo se denominan "funciones miembro constantes" o "inspectores". Las funciones miembro sin constsufijo se denominan "funciones miembro no constantes" o "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
}

El intento de llamar unchangeable.mutate()es un error detectado en tiempo de compilación. No hay tiempo de ejecución ni penalización de velocidad const, y no necesita escribir casos de prueba para verificarlo en tiempo de ejecución.

El seguimiento constde la inspect()función miembro debe usarse para significar que el método no cambiará el estado abstracto del objeto (visible para el cliente). Eso es ligeramente diferente de decir que el método no cambiará los "bits en bruto" de la estructura del objeto. Los compiladores de C ++ no pueden tomar la interpretación "bit a bit" a menos que puedan resolver el problema de alias, que normalmente no se puede resolver (es decir, podría existir un alias no constante que podría modificar el estado del objeto). Otra idea (importante) de este problema de alias: señalar un objeto con un puntero a constante no garantiza que el objeto no cambie; simplemente promete que el objeto no cambiará a través de ese puntero .

qwr
fuente