C ++ Возвращение ссылки на локальную переменную - PullRequest
98 голосов
/ 10 января 2011

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

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

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

Ответы [ 3 ]

166 голосов
/ 10 января 2011

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

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 и / или в заводскую функцию, чтобы вы не делалине нужно 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();    
}

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

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's not legal C++ because reference is not const.
    // big_object& o = func4();  
}
15 голосов
/ 16 июня 2015

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

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

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

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

1 голос
/ 26 апреля 2016

Хорошо помнить, что эти простые правила применимы как к параметрам, так и к типам возвращаемых данных ...

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

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...