возвращение локальных переменных - PullRequest
1 голос
/ 22 июня 2011

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

 int * somefunc()
 {
     int x = 5;
     return &x;
 }

 int * y = somefunc();
 //do something

плохо, небезопасно и т. Д. Я полагаю, что дело обстоит так же для ...

int * somefunc()
{
    int * x = new int;
    x = 5;
    return x;
}

int * y = somefunc();
//do something
delete y;

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

Ответы [ 8 ]

8 голосов
/ 22 июня 2011

В нынешнем виде второй пример неверен.Вы, вероятно, имели в виду это:

int * somefunc()
{
    int * x = new int;
    *x = 5; // note the dereferencing of x here
    return x;
}

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

Рекомендуется вернуть интеллектуальный указатель, например boost::shared_ptr.Это решило бы проблемы, упомянутые выше.Чтобы понять почему, прочитайте о RAII .

1 голос
/ 22 июня 2011

Да, вы рискуете потерять память. (компиляция ошибок в стороне.) Делать это для int глупо, но принцип тот же, даже если это большая структура.

Но поймите: вы написали код в стиле C, где у вас есть функция, выделяющая память.

Если вы пытаетесь изучать C ++, вы должны поместить somefunc () и данные, с которыми он работает, в класс. Методы и данные вместе. Класс также может выполнять RAII, как указано в Space_C0wb0y.

1 голос
/ 22 июня 2011

Первый подход, безусловно, приводит к проблемам, как вы теперь хорошо знаете.

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

void somefunc(int& value)
{
    value = 5;
}

// some code that calls somefunc()
int a_value = 0;
somefunc(a_value);
// printing a_value will display 5 
1 голос
/ 22 июня 2011

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

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

1 голос
/ 22 июня 2011

(Edited)

Да, со вторым все в порядке, если вы разыменовываете «x» перед назначением!

0 голосов
/ 23 июня 2011

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

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

В C ++ мы предпочитаем по возможности избегать необработанных указателей. Чтобы «вернуть уже существующее значение» (т. Е. Функция не создает новое значение), используйте ссылку. Чтобы «вернуть значение, которого еще не было» (т. Е. Функция создает новое значение, в идиоматическом смысле, а не в смысле ключевого слова * 1005), используйте значение или, если необходимо, какую-нибудь интеллектуальную оболочку указателя. 1006 *

0 голосов
/ 22 июня 2011

Хорошо, я бы проанализировал это, ответив на следующие вопросы:

  1. Что содержит x?- Место в памяти (поскольку оно является переменной-указателем)
  2. Какова область действия x?- Поскольку это автоматическая переменная, ее область действия ограничена функцией somefunc ()
  3. Что происходит с автоматическими переменными после выхода из локальной области?- Они удаляются из стека пространства.
  4. Так что теперь происходит с x после возврата из somefunc ()?- Поскольку это автоматическая переменная, объявленная в стеке, ее область действия (время жизни) ограничена somefunc () и, следовательно, будет удалена.
  5. Хорошо, теперь, что происходит со значением, на которое указывает x?У нас есть утечка памяти, поскольку значение выделяется в куче, и мы только что потеряли адрес, когда x удален.
  6. Что вы получаете?- Без понятия.
  7. Что происходит при удалении y?- Понятия не имею.
0 голосов
/ 22 июня 2011

Это и утечка памяти, и сбой (из-за удаления).

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