Почему нормально возвращать вектор из функции?
Пожалуйста, рассмотрите этот код. Я видел этот тип кода несколько раз. words
является локальным вектором. Как можно вернуть его из функции?
Можем ли мы гарантировать, что он не умрет?
std::vector<std::string> read_file(const std::string& path)
{
std::ifstream file("E:\\names.txt");
if (!file.is_open())
{
std::cerr << "Unable to open file" << "\n";
std::exit(-1);
}
std::vector<string> words;//this vector will be returned
std::string token;
while (std::getline(file, token, ','))
{
words.push_back(token);
}
return words;
}
Ответы
Ответ 1
Можем ли мы гарантировать, что он не умрет?
До тех пор, пока ссылка не будет возвращена, это прекрасно подходит для этого. words
будет перенесен в переменную, получающую результат.
Локальная переменная выйдет за пределы области видимости. после его перемещения (или копирования).
Ответ 2
До С++ 11:
Функция не возвращает локальную переменную, а скорее ее копию. Однако ваш компилятор может выполнить оптимизацию, когда не выполняется никакого действительного действия копирования.
Смотрите этот вопрос & ответьте для получения дополнительной информации.
С++ 11:
Функция переместит значение. См. этот ответ для получения дополнительной информации.
Ответ 3
Я думаю, что вы ссылаетесь на проблему в C (и С++), которая возвращает массив из функции, не разрешается (или, по крайней мере, не будет работать так, как ожидалось) - это потому, что возврат массива будет (если вы напишите его в простой форме) верните указатель на фактический массив в стеке, который затем быстро удаляется, когда функция возвращается.
Но в этом случае он работает, потому что std::vector
является классом, а классы, такие как structs, могут (и будут) копироваться в контекст вызывающих. [На самом деле, большинство компиляторов оптимизируют этот тип копии с помощью чего-то, называемого "Оптимизация возвращаемого значения", специально введенного, чтобы избежать копирования больших объектов, когда они возвращаются из функции, но что оптимизация и с точки зрения программистов она будет вести себя так, как если бы для объекта был вызван конструктор присваивания]
Пока вы не возвращаете указатель или ссылку на то, что находится внутри возвращаемой функции, вы в порядке.
Ответ 4
Чтобы хорошо понять поведение, вы можете запустить этот код:
#include <iostream>
class MyClass
{
public:
MyClass() { std::cout << "run constructor MyClass::MyClass()" << std::endl; }
~MyClass() { std::cout << "run destructor MyClass::~MyClass()" << std::endl; }
MyClass(const MyClass& x) { std::cout << "run copy constructor MyClass::MyClass(const MyClass&)" << std::endl; }
MyClass& operator = (const MyClass& x) { std::cout << "run assignation MyClass::operator=(const MyClass&)" << std::endl; }
};
MyClass my_function()
{
std::cout << "run my_function()" << std::endl;
MyClass a;
std::cout << "my_function is going to return a..." << std::endl;
return a;
}
int main(int argc, char** argv)
{
MyClass b = my_function();
MyClass c;
c = my_function();
return 0;
}
Вывод следующий:
run my_function()
run constructor MyClass::MyClass()
my_function is going to return a...
run constructor MyClass::MyClass()
run my_function()
run constructor MyClass::MyClass()
my_function is going to return a...
run assignation MyClass::operator=(const MyClass&)
run destructor MyClass::~MyClass()
run destructor MyClass::~MyClass()
run destructor MyClass::~MyClass()
Ответ 5
Я не согласен и не рекомендую возвращать vector
:
vector <double> vectorial(vector <double> a, vector <double> b)
{
vector <double> c{ a[1] * b[2] - b[1] * a[2], -a[0] * b[2] + b[0] * a[2], a[0] * b[1] - b[0] * a[1] };
return c;
}
Это намного быстрее:
void vectorial(vector <double> a, vector <double> b, vector <double> &c)
{
c[0] = a[1] * b[2] - b[1] * a[2]; c[1] = -a[0] * b[2] + b[0] * a[2]; c[2] = a[0] * b[1] - b[0] * a[1];
}
Я тестировал Visual Studio 2017 со следующими результатами в режиме выпуска:
8.01 СС по ссылке
5.09 СС возвращают вектор
В режиме отладки все гораздо хуже:
0,053 MOPS по ссылке
0,034 СС по возвратному вектору
Ответ 6
Это на самом деле провал дизайна. Вы не должны использовать возвращаемое значение для чего-либо, не являющегося примитивом для чего-либо, что не является относительно тривиальным.
Идеальное решение должно быть реализовано через возвращаемый параметр с решением о ссылке/указателе и надлежащим использованием "const\'y \' ness" в качестве дескриптора.
Кроме того, вы должны понимать, что метка на массиве в C и C++ фактически является указателем, а его подписка - смещением или дополнительным символом.
Таким образом, метка или ptr array_ptr === метка массива, возвращающая таким образом foo [offset], действительно говорит о возвращаемом элементе в месте указателя памяти foo + смещение типа возвращаемого типа.