Подтвердить что ты не робот

С++ - const members/return const int & vs return int

Что мы понимаем под этими линиями С++? Существуют ли альтернативные способы их написания?

const int& a() const;
int getA() const;

Спасибо.

4b9b3361

Ответ 1

Эти два являются двумя возможными сигнатурами для функции-члена в классе, который promises не изменяет сам объект. В первом случае он вернет постоянную ссылку на целое число (возможно, член-атрибут), ссылка const означает, что вызывающий абонент не сможет использовать его для изменения внутреннего атрибута. Второй случай возвращает целое значение по значению.

В семантике есть небольшие различия, но в большинстве случаев они не будут важны, рассмотрите их как две функции для получения значения. Для случая, когда это будет иметь значение, см. Это:

class test {
public:
   test() : m_value() {
      std::cout << &m_value << std::endl; // print where the attribute is
   }
   int const & getValue() const {
      return m_value;
   }
   int copyValue() const {
      return m_value;
   }
   void setValue( int value ) {
      m_value = value;
   }
private:
   int m_value;
};
int main() {
   test t;                      // will printout an address [1]
   int v1 = t.getValue();       // caller copies the value
   int v2 = t.copyValue();      // caller copies the value (itself a copy in hte calle)
   int const &r = t.getValue(); // reference to t.m_value
   int const &c = t.copyValue();// reference to *copy* [2]
   std::cout << v1 << v2 << r << c
      << std::cout;             // 0000
   std::cout << &v1 << &v2      // 4 pointers, the third is [1] a r *is* t.m_value
      << &r << &c << std::cout; //     the rest should be different
   t.setValue( 5 );
   std::cout << v1 << v2 << r   // 0050, v1 and v2 where copies, r *is* t.m_value
      << c << std::cout;
}

Строка, помеченная знаком [2], использует странную особенность языка, с помощью которой, если вы получаете постоянную ссылку на r-значение (временное), компилятор привяжет это временное значение к ссылке и сохранит его до тех пор, пока ссылка не появится (в основном это временное превращение r-значения в скрытую переменную и привязывает ссылку к нему).

Я добавил, что строка должна быть явной в том, что разница в поведении не связана с тем, что принимающая сторона main делает копию сохранения ссылки, а точнее (точнее) сигнатурой аксессора.

Ответ 2

Оба являются эквивалентными способами достижения одного и того же:

const int& a() const;
int getA() const;

Вы возвращаете значение. const справа от заголовка метода - это замечание о том, что функции getA() и a() не будут изменять объект, который их выполнит (скрытый этот параметр). Это важно во время компиляции, поскольку это означает, что во время компиляции учитываются дополнительные проверки. Во время выполнения нет никакой разницы между этими функциями выше и следующими:

const int& a();
int getA();

Однако огромные преимущества расширения возможностей проверки компилятора (ничто не изменяется, когда вы не ожидаете, что это произойдет), очевидно, стоит дополнительный const.

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

std::string getName() const;
   { return name; }

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

std::string &getName() const
   { return name; }

Это действительно очень интересно: мы возвращаем ссылку, а не копию объекта. Ссылка похожа на указатель, поэтому вам придется копировать только указатель (4 байта в 32-битной системе) вместо всего объекта. Это многообещающе. Однако он даже не компилируется. Компиляция будет жаловаться на то, что вы возвращаете ссылку, пока вы обещали, что метод будет const, и поэтому объект, который он будет выполнять, не должен изменяться. Этот код допускает незаконные операции:

Person p( "Baltasar" );
p.getName() = "José";

cout << p.getName() << endl;

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

const std::string &getName() const
    { return name; }

Теперь он будет компилироваться, а предыдущий, злоумышленный код не будет. Теперь вернемся к нашей проблеме:

const int &getA() const;
int a() const;

Второй - это возврат к значению, что означает, что int (4 байта) будет скопирован при возврате. Первый означает, что будет возвращена постоянная ссылка на int (4 байта). Как видно, в этом случае нет выгоды от производительности для использования return-by-reference вместо return-by-value.

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

Ответ 3

const int& a() const;

a() возвращает ссылку const на int. Модификатор const в конце означает, что он не может изменить состояние объекта, на который он вызван.

int getA() const;

То же, что и вышеприведенное описание, за исключением того, что тип возврата int, который включает в себя копию возвращаемой переменной, если она собрана.

Что означает, когда сказано, что не может изменить состояние объекта?

class foo
{
    int m_Var ;
    public:
        foo(int arg1) : m_Var(arg1){}
        void mutableMethod()
        {
            m_Var = 20 ;  // "this" has the variable m_Var and
                          //  by assigning it a value changes the state of
                          //  m_Var. Changing the state of it member variable
                          //  is meant changing the state of object.
        }

        void nonMutableMethod() const
        {
             m_Var = 20 ;  //  This assignment is not allowed because of const 
                           //  modifier. The method is not allowed to change the
                           //  the state of object on which it is called ( this )
        }
};

Кроме того, постоянные методы не могут возвращать переменные-члены с помощью неконстантной ссылки.

Ответ 4

Важнейшим отличием является то, что:

  • getA() возвращает значение int данных, которое затем может использоваться вызывающим пользователем, полностью независимым от любой другой части программы
  • a() возвращает ссылку на некоторый int, который a() выбирает:
    • int x = a() "sample" значение int в это время и логически эквивалентно int x = getA()
    • const int& x = a() сохраняет ссылку на переменную, возвращаемую функцией()!

Сохранение ссылок не всегда делает то, что вы ожидаете или хотите

  • ХОРОШО: компилятор достаточно умен, чтобы сделать копию этой переменной, если она была временным/литералом (например, const int& x = a(), const int& a() { return 3; })

  • GOOD или BAD? (в зависимости от того, имеет ли это смысл в приложении): каждый раз, когда значение x читается позже, программа может (пытаться) перечитать он из исходной переменной int, которая a() внутренне возвращается: если это значение переменной с тех пор было изменено, тогда значение x также изменится. ( "может", потому что оптимизатор может избежать этого, когда значение будет одинаковым в любом случае)

  • UGLY: если память на этом адресе больше не хранит эту переменную (например, она была в new ed memory, которая с delete d), а затем пытается чтение значения x может привести к непредсказуемому значению или сбою приложения (если адрес памяти больше не читается).

Оба a() и getA() являются функциями-членами класса; мы знаем это, потому что только функции-члены могут быть const, что технически указывает, что они не могут изменять элементы данных не mutable, не отбрасывая их константу, но целью этого ограничения является то, что они не должны изменять код вызывающего абонента, наблюдаемое значение объекта; изменяемые данные обычно используются для кэширования, отладки и т.д.

Ответ 5

Мы можем привести возвращенную ссылку к указателю, поэтому теоретически это делает больше информации доступной (адрес и значение), чем возвращая копию (значение).

И это можно взломать const ref для mutable с const_cast.

Компилятор все равно попытается использовать значение из исходного регистра, адреса или литерала.

Альтернативные пути для какой цели? Чтобы быть уверенным в том, что постоянное пребывание правильно, не добавит лишней работы. Для справки const и const я нахожу макросы CR & CN удобными.

#define CN  const
#define CR  const&         // Constant reference
#define CDa const*         // mutable pointer to constant data
#define CPD const * const  // constant pointer to constant data
const int&  verbose() const;
int CR      shorter() CN;

Побочным эффектом является то, что объявления становятся короче, а по мере сокращения строк количество строк также уменьшается. Дело вкуса, хотя... Но в сочетании с макросом DMAP это, кажется, имеет преимущество.

typedef std::map<size_t, float>     TypeMap_Of_size_t_vs_float;
TypeMap_Of_size_t_vs_float          m_Map;
const TypeMap_Of_size_t_vs_float&   verboseIsNice() const
{
    return m_MyMap;
}
for each (auto myElement in verboseIsNice()) 
{
    myElement.foo();
}

против

DMAP(SZ, Flo)  m_Map;                    // typedefs MSZFlo=std::map<size_t, float>
MSZFlo CR      tldr()                 CN { return m_Map; }
fe(el, tldr()) el.foo();

Без auto и с использованием итераторов пример показал бы более 333% разницы.