Каковы последствия возврата значения как константы, ссылки и константы в C ++? - PullRequest
13 голосов
/ 28 апреля 2009

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

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

Ответы [ 8 ]

27 голосов
/ 28 апреля 2009

Ниже приводится описание всех ваших дел:

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

a[i] = 7;

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

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

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

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

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

if (someFunction() = 2)

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

if (someFunction() == 2)

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

const int someFunction()

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

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

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";

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

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

6 голосов
/ 28 апреля 2009

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

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

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

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

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

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

1 голос
/ 28 апреля 2009
const int exampleOne();

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

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

int& exampleTwo();

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

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

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

const int& exampleThree();

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

1 голос
/ 28 апреля 2009
  • Возврат по ссылке .

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

#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 "указывают" на одно и то же значение, поэтому при изменении b изменяется значение obj.a.

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

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

constA()++;

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

  • Возвращает постоянную ссылку .

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

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

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

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

1 голос
/ 28 апреля 2009

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

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

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

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

0 голосов
/ 28 апреля 2009

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

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

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

Относительно ключевого слова 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 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: C ++ 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.

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

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

const int exampleOne();

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

int& exampleTwo();

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

const int& exampleThree();

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

0 голосов
/ 28 апреля 2009

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

const int exampleOne();

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

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

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

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

0 голосов
/ 28 апреля 2009

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

void func(const int& a);

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

...