С++ Возвращаемая ссылка на локальную переменную

Правилен ли следующий код (func1()), если он должен вернуть i? Я помню, где-то читал, что возникает проблема при возврате ссылки на локальную переменную. Чем он отличается от func2()?

int& func1()
{
    int i;
    i = 1;
    return i;
}

int* func2()
{
    int* p;
    p = new int;
    *p = 1;
    return p;
}

Ответ 1

Этот фрагмент кода:

int& func1()
{
    int i;
    i = 1;
    return i;
}

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

int main()
{
    int& p = func1();
    /* p is garbage */
}

Вторая версия работает, потому что переменная выделяется в свободном хранилище, которая не привязана к времени жизни вызова функции. Однако вы несете ответственность за delete выделение int.

int* func2()
{
    int* p;
    p = new int;
    *p = 1;
    return p;
}

int main()
{
    int* p = func2();
    /* pointee still exists */
    delete p; // get rid of it
}

Обычно вы переносите указатель в класс RAII и/или функцию factory, поэтому вам не нужно delete это сами.

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

int func3()
{
    return 1;
}

int main()
{
    int v = func3();
    // do whatever you want with the returned value
}

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

class big_object 
{ 
public:
    big_object(/* constructor arguments */);
    ~big_object();
    big_object(const big_object& rhs);
    big_object& operator=(const big_object& rhs);
    /* public methods */
private:
    /* data members */
};

big_object func4()
{
    return big_object(/* constructor arguments */);
}

int main()
{
     // no copy is actually made, if your compiler supports RVO
    big_object o = func4();    
}

Интересно, что привязка временной ссылки к const является совершенно законным С++.

int main()
{
    // This works! The returned temporary will last as long as the reference exists
    const big_object& o = func4();    
    // This does *not* work! It not legal C++ because reference is not const.
    // big_object& o = func4();  
}

Ответ 2

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

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

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

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

Ответ 3

Хорошо помнить эти простые правила, и они применяются к обоим параметрам и типам возврата...

  • Значение - копирует данный предмет.
  • Указатель - ссылается на адрес рассматриваемого элемента.
  • Ссылка - это буквально предмет, о котором идет речь.

Для каждого есть время и место, поэтому убедитесь, что вы их знаете. Локальные переменные, как вы показали здесь, просто таковы, что они ограничены временем, когда они локально живы в области функций. В вашем примере с возвращаемым типом int* и возвратом &i было бы одинаково некорректно. Вам было бы лучше в этом случае сделать это...

void func1(int& oValue)
{
    oValue = 1;
}

Это приведет к прямому изменению значения вашего переданного параметра. В то время как этот код...

void func1(int oValue)
{
    oValue = 1;
}

не будет. Он просто изменит значение oValue local на вызов функции. Причина этого заключается в том, что вы фактически меняете только "локальную" копию oValue, а не oValue.

Ответ 4

> int &f(); int x;
> 
> int main() {
>     f() = 100;
>     cout<<x;
>     return 0; }
> 
> int &f() {
>      int x;
>      return x; }

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