Каковы последствия возврата значения как константы, ссылки и константы в С++?

Я изучаю С++, и я до сих пор смущен этим. Каковы последствия возврата значения как константы, ссылки и константы в С++? Например:

const int exampleOne();
int& exampleTwo();
const int& exampleThree();

Ответы

Ответ 1

Здесь приведено описание всех ваших случаев:

• Возврат по ссылке: вызов функции может использоваться как левая сторона задания. например используя перегрузку оператора, если вы перегружены оператором [], вы можете сказать что-то вроде

a[i] = 7;

(при возврате по ссылке вам нужно убедиться, что возвращаемый объект доступен после возврата: вы не должны возвращать ссылку на локальный или временный)

• Возврат как постоянное значение. Предотвращает использование функции в левой части выражения присваивания. Рассмотрим перегруженный оператор+. Можно написать что-то вроде:

a + b = c; // This isn't right

Наличие возвращаемого типа оператора + как "const SomeType" позволяет возвратить по значению и в то же время не позволяет использовать выражение в левой части задания.

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

if (someFunction() = 2)

когда вы имели в виду

if (someFunction() == 2)

Если someFunction() объявлен как

const int someFunction()

то описанная выше надстройка if() была бы захвачена компилятором.

• Возврат в качестве постоянной ссылки. Этот вызов функции не может отображаться в левой части задания, и вы хотите избежать копирования (возврат по значению). Например. скажем, у нас есть класс Student, и мы хотели бы предоставить идентификатор доступа(), чтобы получить идентификатор студента:

class Student
{
    std::string id_;

public:

    const std::string& id() const;
};

const std::string& Student::id()
{
    return id_;
}

Рассмотрим вспомогательный модуль id(). Это должно быть объявлено const, чтобы гарантировать, что функция члена id() не будет изменять состояние объекта. Теперь рассмотрим тип возврата. Если тип возврата был string & то можно написать что-то вроде:

Student s;
s.id() = "newId";

что мы не хотим.

Мы могли бы вернуться по значению, но в этом случае возврат по ссылке более эффективен. Создание возвращаемого типа const const & дополнительно предотвращает изменение идентификатора.

Ответ 2

Основная вещь, которую нужно понять, - это то, что при возврате по значению создаст новую копию вашего объекта. Возврат по ссылке вернет ссылку на существующий объект. ПРИМЕЧАНИЕ. Так же, как указатели, вы можете иметь болтающиеся ссылки. Таким образом, не создавайте объект в функции и не возвращайте ссылку на объект - он будет уничтожен, когда функция вернется, и она вернет ссылку на оборку.

Возврат по значению:

  • Когда у вас есть POD (Обычные старые данные)
  • Если вы хотите вернуть копию объекта

Возврат по ссылке:

  • Когда у вас есть причина в производительности, чтобы избежать копирования объекта, который вы возвращаете, и вы понимаете время жизни объекта
  • Когда вы должны вернуть конкретный экземпляр объекта, и вы понимаете время жизни объекта

Константы/константные ссылки помогают вам выполнять контракты вашего кода и помогают компиляторам ваших пользователей находить ошибки использования. Они не влияют на производительность.

Ответ 3

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

Обратите внимание, что в С++, если вы не говорите, что что-то является ссылкой или указателем, это значение, поэтому вы создадите новую копию, а не изменяете исходный объект. Это может быть не совсем очевидно, если вы придете с других языков, использующих ссылки по умолчанию.

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

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

Ответ 4

  • Возврат по ссылке.

Вы можете вернуть ссылку на некоторое значение, например член класса. Таким образом, вы не создаете копии. Однако вы не должны возвращать ссылки на значения в стеке, поскольку это приводит к поведению undefined.

#include <iostream>                                                                                                                                          
using namespace  std;


class A{
private: int a;

public:
        A(int num):a(num){}

        //a to the power of 4.
        int& operate(){
                this->a*=this->a;
                this->a*=this->a;
                return this->a;
        }

        //return constant copy of a.
        const int constA(){return this->a;}

        //return copy of a.
        int getA(){return this->a;}
};

int main(){

        A obj(3);
        cout <<"a "<<obj.getA()<<endl;
        int& b=obj.operate(); //obj.operate() returns a reference!

        cout<<"a^4 "<<obj.getA()<<endl;
        b++;
        cout<<"modified by b: "<<obj.getA()<<endl;
        return 0;
}

b и obj.a "point" к одному и тому же значению, поэтому модификация b изменяет значение obj.a.

$./a.out 
a 3
a^4 81
modified by b: 82
  • Возвращает значение const.

С другой стороны, возврат значения const означает, что указанное значение не может быть изменено. Следует отметить, что возвращаемое значение является копией.: Например,

constA()++;

приведет к ошибке компиляции, поскольку копия, возвращаемая константой(), является постоянной. Но это всего лишь копия, это не означает, что A:: a является постоянным.

  • Вернуть ссылку const.

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

const int& refA(){return this->a;}

const int& b = obj.refA();
b++;

приведет к ошибке компиляции.

Ответ 5

const int exampleOne();

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

Это может иметь значение для более сложных типов, причем модификация их может иметь нежелательные побочные эффекты. (Понятно, что функция возвращает объект, представляющий дескриптор файла.Если этот дескриптор const, файл доступен только для чтения, в противном случае его можно изменить. Тогда в некоторых случаях функция возвращает значение const. Но в общем случае возвращение значения const является необычным.

int& exampleTwo();

Этот возвращает ссылку на int. Это не влияет на время жизни этого значения, поэтому это может привести к поведению undefined в таком случае:

int& exampleTwo() {
  int x = 42;
  return x;
}

мы возвращаем ссылку на значение, которое больше не существует. Компилятор может предупредить вас об этом, но он, вероятно, будет компилироваться. Но это бессмысленно и рано или поздно вызовет фанки. Однако это часто используется в других случаях. Если функция была членом класса, она могла бы вернуть ссылку на переменную-член, срок жизни которой будет длиться до тех пор, пока объект не выйдет из области действия, что означает, что возвращаемое значение функции остается действительным, когда функция возвращается.

const int& exampleThree();

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

Ответ 6

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

void func(const int& a);

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

Ответ 7

Ваш первый случай:

const int exampleOne();

С простыми типами, такими как int, это почти никогда не то, что вы хотите, потому что const бесполезно. Возврат по значению означает копию, и вы можете свободно назначать неконстантный объект:

int a = exampleOne(); // perfectly valid.

Когда я вижу это, это обычно потому, что тот, кто написал код, пытался быть const-правильным, что похвально, но не совсем понимало последствия того, что они пишут. Однако есть случаи с перегруженными операторами и пользовательскими типами, где это может иметь значение.

Некоторые компиляторы (новые GCC, Metrowerks и т.д.) предупреждают о поведении, подобном этому, с простыми типами, поэтому его следует избегать.

Ответ 8

Я думаю, что на ваш вопрос на самом деле два вопроса:

  • Каковы последствия возврата константы.
  • Каковы последствия возврата ссылки.

Чтобы дать вам лучший ответ, я расскажу немного больше об обеих концепциях.

Что касается ключевого слова const

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

MyObject *o1 = new MyObject;
const MyObject *o2 = o1;
o1->set(...); // Will work and will change the instance variables.
o2->set(...); // Won't compile.

Теперь ключевое слово const может использоваться в трех разных контекстах:

  • Обеспечение вызова вызывающего метода, который вы не будете изменять объект

Например:

void func(const MyObject &o);
void func(const MyObject *o);

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

  • Обеспечение компилятором того, что конкретный метод не мутирует объект

Если у вас есть класс и некоторые методы, которые "получают" или "получают" информацию из переменных экземпляра без их модификации, я должен использовать их, даже если используется ключевое слово const. Например:

class MyObject
{
    ...
public:
    void setValue(int);
    int getValue() const; // The const at the end is the key
};

void funct(const MyObject &o)
{
    int val = o.getValue(); // Will compile.
    a.setValue(val); // Won't compile.
}
  • Наконец, (ваш случай), возвращающий значение const

Это означает, что возвращенный объект не может быть изменен или изменен напрямую. Например:

const MyObject func();

void func2()
{
    int val = func()->getValue(); // Will compile.
    func()->setValue(val); // Won't compile.
    MyObject o1 = func(); // Won't compile.
    MyObject o2 = const_cast<MyObject>(func()); // Will compile.
}

Дополнительная информация о ключевом слове const: С++ Faq Lite - Const Correctness

Что касается ссылок

Возврат или получение ссылки означает, что объект не будет дублироваться. Это означает, что любое изменение, внесенное в само значение, будет отражено вне области функции. Например:

void swap(int &x, int &y)
{
    int z = x;
    x = y;
    y = z;
}
int a = 2; b = 3;
swap(a, b); // a IS THE SAME AS x inside the swap function

Итак, возврат ссылочного значения означает, что значение может быть изменено, например:

class Foo
{
public:
    ...
    int &val() { return m_val; }
private:
    int m_val;
};

Foo f;
f.val() = 4; // Will change m_val.

Дополнительная информация о ссылках: С++ Faq Lite - семантика ссылок и значений

Теперь, отвечая на ваши вопросы

const int exampleOne();

означает, что возвращаемый объект не может измениться через переменную. Это более полезно при возврате объектов.

int& exampleTwo();

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

const int& exampleThree();

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