Функция в C ++ возвращает по значению или по ссылке? - PullRequest
18 голосов
/ 17 сентября 2011

Когда функция (вызываемая) возвращает количество функции вызывающей функции, возвращается ли она значение или по ссылке?

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

Я сомневался, потому что мне сказали, что когда функция C ++ возвращается и завершается, все переменные / память, связанные с этой функцией, очищаются.

struct node{

string key;
int pnum;
node* ptr;
}

vector< vector<node> > myfun1(/*Some arguments*/)
{

/*Build the vector of vectors. Call it V*/

return v;

}

int main(void)
{
a=myfun1(/* Some arguments */)
}

Ответы [ 7 ]

26 голосов
/ 17 сентября 2011

Функции C ++ могут возвращать по значению, по ссылке (но не возвращать локальную переменную по ссылке) или по указателю (опять же, не возвращать локальную по указателю).

При возврате позначение, компилятор часто может делать оптимизации, которые делают его одинаково быстрым, как возвращение по ссылке, без проблемы висячих ссылок.Эти оптимизации обычно называют «Оптимизацией возвращаемого значения (RVO)» и / или «Именованной оптимизацией возвращаемого значения (NRVO)».

Другой способ для вызывающего абонента предоставить пустой вектор (посредством ссылки)функция заполняет его. Тогда ему не нужно ничего возвращать.

Вам определенно следует прочитать это сообщение в блоге: Хотите скорость?Передать по значению.

25 голосов
/ 17 сентября 2011

По умолчанию все в C / C ++ передается по значению, включая тип возвращаемого значения, как в примере ниже:

T foo() ;

В C ++, где типы обычно считаются типами-значениями (т.е. ониведут себя как типы int или double), дополнительная копия может быть дорогостоящей, если создание / уничтожение объекта не является тривиальным.

С C ++ 03

Если вы хотите вернутьпо ссылке или по указателю вам нужно изменить тип возвращаемого значения:

T & foo() ;  // return a reference
T * foo() ;  // return a pointer

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

Если вы не можете до сих пор гарантировать объектсуществует после возврата, ваше единственное решение - либо:

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

void foo(T & t) ;

Таким образом, внутри функции вы устанавливаете значение t по мере необходимостии после возврата из функции у вас есть результат.

С C ++ 11

Теперь, если у вас есть возможность работать с C ++ 0x / C ++ 11, тос компилятором, который поддерживает r-значения, ссылки / семантика перемещения , если ваш объект имеет правильный конструктор / оператор (если ваш объект взят из стандартной библиотеки, тогда все в порядке), тогда дополнительная временная копиябудет оптимизирован, и вы могли быи сохраните запись:

T foo() ;

Зная, что компилятор не сгенерирует ненужное временное значение.

4 голосов
/ 17 сентября 2011

Возвращается тем, что вы объявляете типом возврата. vector<int> f(); и vector<int>& f(); возвращают значения и ссылки соответственно. Однако было бы серьезной ошибкой возвращать ссылку на локальную переменную в функции, поскольку она будет удалена при выходе из области действия функции.

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

2 голосов
/ 17 сентября 2011

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

std::vector<int> my_func(); // returns value
std::vector<int>& my_func(); // returns reference
std::vector<int> const& my_func(); // returns constant reference

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

Если вы хотите «создать» большой вектор внутри функции, а затем вернуть его без копирования, самый простой способ - передать вектор в функцию в качестве ссылочного параметра:

void fill_vector(std::vector<int> &vec) {
    // fill "vec" and don't return anything...
}

Такжеобратите внимание, что в недавно ратифицированной новой версии стандарта C ++ (известной как C ++ 0x или C ++ 11) возвращение локального вектора по значению из функции не фактически скопирует вектор, это будет переместился на новое место.Код, который делает это, выглядит идентичным коду из предыдущих версий C ++, который можно принудительно скопировать вектор.Проверьте у своего компилятора, поддерживает ли он «семантику перемещения» (часть стандарта C ++ 11, которая делает это возможным).

2 голосов
/ 17 сентября 2011

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

http://cpp -next.com / Архив / 2009/08 / хотите-скорость-передача по значению /

0 голосов
/ 30 января 2014

Как и большинство вещей в C ++, ответ таков: «это зависит от того, как вы определили функцию».

По умолчанию для языка используется возврат по значению.Простой вызов типа double f () всегда будет возвращать число с плавающей точкой по значению.Однако вы МОЖЕТЕ вернуть значения по указателю или по ссылке - вы просто добавляете дополнительные символы '&' или '*' к типу возвращаемого значения:

// Return by pointer (*)
T* f();

// Return by reference (a single '&')
T& f();

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

// Return by 'parameter' (a logical reference return)
void f(T& output);

Теперь у выхода есть реальное имя, и мы ЗНАЕМ, что он переживет вызов, потому что долженсуществовать до того, как будет сделан вызов 'f'.Это шаблон, который вы часто будете видеть в C ++, особенно для таких вещей, как заполнение STL std :: vector.Это ужасно, но до появления C ++ 11 это часто было быстрее, чем просто возвращать вектор по значению.Теперь, когда возвращение по значению стало проще и быстрее даже для многих сложных типов, вы, вероятно, не увидите много функций, следующих за эталонным шаблоном возвращаемого параметра за пределами старых библиотек.

0 голосов
/ 17 сентября 2011

Все переменные, определенные в стеке, очищаются при выходе. Чтобы вернуть переменную, вы должны разместить ее в куче, что вы делаете с новым ключевым словом (или malloc).

Классы и структуры передаются как указатели, а примитивные типы передаются как значения.

...