8

Что означает 'const' в конце объявления метода класса?

6

Вопрос: Каково значение const в таких объявлениях?

У меня есть следующий фрагмент кода на C++:

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

Я не совсем понимаю, какое значение имеет const в этих объявлениях. Что конкретно означает const после определения операторов и методов в классе? Как это влияет на поведение объектов данного класса? Буду признателен за объяснение!

5 ответ(ов)

11

Когда вы добавляете ключевое слово const к методу, указатель this фактически становится указателем на const объект, и вы не сможете изменить данные членов этого объекта. (За исключением случаев, когда вы используете mutable, но об этом позже).

Ключевое слово const является частью сигнатуры функции, что означает, что вы можете реализовать два похожих метода: один, который вызывается, когда объект является const, и другой, который не является таковым.

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

Это выведет:

Foo
Foo const

В методе без const вы можете изменять члены экземпляра, чего нельзя сделать в версии с const. Если вы измените объявление метода в приведенном выше примере на следующий код, вы получите ошибки.

    void Foo()
    {
        counter++; // это работает
        std::cout << "Foo" << std::endl;    
    }

    void Foo() const
    {
        counter++; // это не скомпилируется
        std::cout << "Foo const" << std::endl;
    }

Это не совсем верно, потому что вы можете пометить член как mutable, и тогда метод const сможет его изменять. Это обычно используется для внутренних счетчиков и подобных вещей. Решение для этого будет следующим кодом.

#include <iostream>

class MyClass
{
private:
    mutable int counter;
public:

    MyClass() : counter(0) {}

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

    void Foo() const
    {
        counter++;    // Это работает, потому что counter - `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 было вызвано " << ccc.GetInvocations() << " раз" << std::endl;
}

Это выведет:

Foo
Foo const
Foo было вызвано 2 раз
0

Квалификатор const означает, что методы могут быть вызваны для любого значения типа foobar. Разница возникает, когда вы пытаетесь вызвать неконстантный метод на константном объекте. Рассмотрим, если ваш тип foobar имел бы следующее дополнительное объявление метода:

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

Метод bar() является неконстантным и может быть вызван только для неконстантных значений.

void func1(const foobar& fb1, foobar& fb2) {
  const char* v1 = fb1.bar();  // не скомпилируется
  const char* v2 = fb2.bar();  // работает
}

Идея за const заключается в том, чтобы обозначить методы, которые не будут изменять внутреннее состояние класса. Это мощная концепция, но на самом деле она не является строго соблюдаемой в C++. Это скорее обещание, чем гарантия. И это обещание часто нарушается и легко нарушается.

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

Ответ Блэра в точку.

Однако стоит отметить, что к членам данных класса можно добавить квалификатор mutable. Любой член, помеченный таким образом, может быть изменён в методе, определённом как const, без нарушения контракта const.

Вы можете использовать это, например, если хотите, чтобы объект запоминал, сколько раз был вызван какой-то определённый метод, при этом не затрагивая "логическую" постоянность этого метода.

0

Я хотел бы добавить следующий пункт.

Вы также можете сделать это const & и const &&

Вот пример:

struct s {
    void val1() const {
        // *this здесь является const, поэтому эта функция не может изменять члены *this
    }
    void val2() const & {
        // *this здесь является const&, значит, доступен только для lvalue-ссылок
    }
    void val3() const && {
        // Этот объект, вызывающий эту функцию, должен быть const rvalue.
    }
    void val4() && {
        // Этот объект, вызывающий эту функцию, должен быть только rvalue-ссылкой.
    }
};

int main() {
    s a;
    a.val1(); // все в порядке
    a.val2(); // все в порядке
    // a.val3() не сработает, a не является rvalue, но если вызвать так:
    std::move(a).val3(); // все в порядке, std::move делает его rvalue
}

Не стесняйтесь улучшать ответ. Я не эксперт.

0

Когда вы используете const в сигнатуре метода (например, const char* foo() const;), вы сообщаете компилятору, что память, на которую указывает this, не может быть изменена внутри этого метода (в данном случае foo). Это означает, что внутри метода вы можете безопасно обращаться к членам класса, не опасаясь их изменения. Кроме того, это позволяет вызывать этот метод на константных объектах данного класса.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь