Значение 'const' последний в объявлении функции класса?

727

В чем смысл таких constобъявлений? constМеня смущает.

class foobar
{
  public:
     operator int () const;
     const char* foo() const;
};
Rakete1111
источник

Ответы:

951

Когда вы добавляете 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версии. Если вы измените объявление метода в приведенном выше примере на приведенный ниже код, вы получите несколько ошибок.

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

    void Foo() const
    {
        counter++; //this will not compile
        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++;    // 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;
}

который бы вывел

Foo
Foo const
Foo has been invoked 2 times
Матс Фредрикссон
источник
187

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

const foobar fb;
fb.foo();

было бы законно.

Посмотрите, сколько и как используется «const» в C ++? за дополнительной информацией.

Блэр Конрад
источник
47

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

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

Метод bar()неконстантный и доступен только из неконстантных значений.

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

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

foobar& fbNonConst = const_cast<foobar&>(fb1);
JaredPar
источник
3
Я думал, что ответ о других методах const, а не о const объектах.
Николай Голубев
Спасибо за «Идея constсостоит в том, чтобы пометить методы, которые не изменят внутреннее состояние класса». Это действительно то, что я искал.
Ковач
1
@JaredPar означает ли это, что любая функция-член, представляющая операцию только для чтения, должна быть помечена как const?
Ковач
26

Это const означает, что компилятор выдаст ошибку, если метод 'с const' изменит внутренние данные.

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

Тест

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
}

Прочитайте это для получения дополнительной информации

Николай Голубев
источник
1
Вопрос о constфункциях-членах, в которых не упоминается изменчивость, в лучшем случае неполон.
IInspectable
13

Блэр ответ на вопрос.

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

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

Альнитак
источник
10

Значение функции-члена Const в C ++. Общеизвестно: Essential Intermediate Programming дает четкое объяснение:

Тип указателя this в неконстантной функции-члене класса X - X * const. То есть это постоянный указатель на непостоянный X (см. Const Pointers и Pointers to Const [7, 21]). Поскольку объект, к которому это относится, не является константой, его можно изменить. Тип этого в функции-члене const класса X - const X * const. То есть это постоянный указатель на константу X. Поскольку объект, к которому это относится, является const, его нельзя изменить. В этом разница между константными и неконстантными функциями-членами.

Итак, в вашем коде:

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

Вы можете думать так:

class foobar
{
  public:
     operator int (const foobar * const this) const;
     const char* foo(const foobar * const this) const;
};
Нань Сяо
источник
thisне const. Причина, по которой он не может быть изменен, заключается в том, что это значение.
Брайан
7

когда вы используете constв сигнатуре метода (как ваш сказал:), const char* foo() const;вы говорите компилятору, что указанная память thisне может быть изменена этим методом (который fooздесь).

Матрица Бастер
источник
6

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

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

Так,

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
}

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

coder3101
источник
1
*thisвсегда является lvalue, даже если функция-член имеет квалификацию rvalue-ref и вызывается для rvalue. Пример .
HolyBlackCat
1
Да, так как мне улучшить свой текущий ответ?
coder3101
Я имею в виду, что написать в комментариях в блоке, который оправдывает поведение
coder3101
Обновлено. Все хорошо?
coder3101
2

СопзИте ключевое слово , используемое с функцией декларация уточняется , что она является функцией члена Const , и он будет не в состоянии изменить элементы данных объекта.

Чандра Шекхар
источник
1

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

Что такоеconst функция-член»?

Функция-член, которая проверяет (а не мутирует) свой объект.

Функция- constчлен обозначается constсуффиксом сразу после списка параметров функции-члена. Функции-члены с constсуффиксом называются «постоянными функциями-членами» или «инспекторами». Функции-члены без constсуффикса называются «неконстантными функциями-членами» или «мутаторами».

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
}

Попытка вызова unchangeable.mutate()является ошибкой, обнаруженной во время компиляции. Нет места для выполнения или потери скорости const, и вам не нужно писать контрольные примеры для проверки во время выполнения.

Продольные constна inspect()функции члена следует использовать для обозначения методы не изменит объект абстрактного (клиент-видимый) состояния. Это немного отличается от того, что метод не изменит «сырые биты» структуры объекта. Компиляторам C ++ не разрешается принимать «побитовую» интерпретацию, если они не могут решить проблему псевдонимов, которая обычно не может быть решена (т. Е. Может существовать неконстантный псевдоним, который может изменять состояние объекта). Другое (важное) понимание этой проблемы с наложением: указание на объект с указателем на const не гарантирует, что объект не изменится; он просто обещает, что объект не изменится через этот указатель .

qwr
источник