Методы Const, возвращающие ссылки

class Foo
{
    int Bar;

    public:

    int& GetBar() const
    {
        return Bar;
    }
}

Хорошо ли, что GetBar - это метод const? На самом деле это ничего не меняет, но обеспечивает "внешний мир" средством его изменения.

Ответы

Ответ 1

У вас есть опечатка в коде, это то, что вы, вероятно, имели в виду:

class Foo
{
    int Bar;

    public:

    int& GetBar() const
    {
        return Bar; // Removed the ampersand, because a reference is returned, not an address
    }
}

И нет, это не законно. При пометке метода с помощью const вы не только обещаете, что не будете касаться какого-либо внутреннего состояния объекта, вы также обещаете, что не вернете ничего, что может быть использовано для изменения состояния объекта. Неконстантная ссылка может использоваться для изменения значения Bar вне области действия GetBar(), поэтому вы подразумеваете, что вы не можете выполнить обещание.

Вы должны либо изменить метод как неконстантный, вернуть ссылку на константу, либо сделать Bar освобожденным от обещания, пометив его как mutable. Например: mutable int Bar; Ключевое слово mutable сообщает компилятору, что логическая константа объекта не зависит от состояния Bar. Тогда вам разрешено делать все, что вам угодно.

Ответ 2

Нет, поскольку вы не можете выполнить следующее задание: const int x; int &y = x;

Что вы можете сделать, это const int x; const int &y = x;

И, конечно же, нет никакой проблемы при перегрузке метода и создании как const, так и неконстантных вариантов.

Ответ 3

Вероятно, вы хотите вернуть Bar, а не &Bar. Во всяком случае, я сбросил этот код в Комо:

class Foo
{
    int Bar;

    public:

    int& GetBar() const
    {
        return &Bar;
    }
};

int main(int argc, char** argv)
{
  Foo x;
  int y = x.GetBar();
  y = 5;
  return 0;
}

И получил ошибку:

line 9: error: qualifiers dropped in binding reference of type
          "int &" to initializer of type "const int"
          return Bar;
                 ^

Ответ 4

Прежде всего, чтобы вернуть ссылку, вам не нужно использовать оператор амперсанда на указанном объекте; это необходимо, если вы хотите получить указатель на него. Итак, я предполагаю, что вы хотели написать return Bar;.

Тогда нет, вы не можете этого сделать; в методе const у вас есть указатель const this (в вашем случае это будет const Foo *), что означает, что любая ссылка, которую вы можете получить в своих полях 1 будет a const, так как вы обращаетесь к ним через "const путь".

Таким образом, если вы попытаетесь сделать то, что вы сделали в этом коде, вы получите ошибку компиляции, так как вы попытаетесь инициализировать int & (возвращаемое значение вашего метода) с помощью const int & ( ссылку, которую вы получаете от Bar), что, очевидно, запрещено.

g++ фактически говорит:

testconstref.cpp: In member function ‘int& Foo::GetBar() const’:
testconstref.cpp:9: error: invalid initialization of non-const reference of type ‘int&’ from a temporary of type ‘const int*’

что я только что сказал. :)

Если вместо этого вы возвращаете ссылку const в поле класса из метода const, у вас не будет проблем.


  • Исключая поля, помеченные как mutable, которые сообщают компилятору, что такие поля могут быть изменены даже с помощью методов const; это исключение было введено, чтобы позволить const методам изменять "реальное" состояние объекта в случаях, когда это не изменяет его "логическое" состояние; это может быть полезно для реализации ленивой оценки, подсчета ссылок,...

Ответ 5

Члены вашего класса считаются const, когда вы находитесь в методе const. Поэтому, хотя Bar int внутри вашего класса, а не const int, в контексте GetBar() const это значение "const int". Поэтому возврат его как неконстантной ссылки или указателя так же незаконно, как и выполнение:

const int y = 57; int& z = y;

Это нарушает const-правильность, хотя вторая строка ничего не изменила (пока) в y.

Обратите внимание, что если у вашего класса есть указатели, единственное, что является const, это сами указатели, а не то, на что они указывают. (Часто называемая "мелкой" консистенцией)

Таким образом, это будет законным:

class A
{
  Foo * foo;

  public:
    Foo * getFoo() const // legal. does not have to return const Foo *
    {
       return foo;
    }
};

Обратите внимание, что в исходном коде вам будет разрешено возвращать Bar по ссылке non-const, если она mutable, потому что эти члены не связаны константой функций-членов.

Ответ 6

const модификатор к функции-члену не позволяет изменять состояние объекта с помощью его области. Компилятор просто проверяет, изменяет ли эта функция состояние объекта или нет в его области. Другой пример -

class foo
{
    int num ;
    public :
       foo( int anum )
       {
           anum = 10;
       }
       int getNum()
       {
           return num;
       }
 };

 foo obj;
 int& myNum = obj.getNum() ; // myNum is just an alias to the private class variable num
 myNum = 40; // Actually changes the content of the private variable.

Таким образом, компилятор просто проверяет спецификаторы доступа (т.е. доступна ли эта переменная или нет в этой области), но не о местоположении переменной памяти private/public/protected, если она возвращается к какой-либо другой переменной.