我需要直截了当。使用下面的代码:
vector<unsigned long long int> getAllNumbersInString(string line){
vector<unsigned long long int> v;
string word;
stringstream stream(line);
unsigned long long int num;
while(getline(stream, word, ',')){
num = atol(word.c_str());
v.push_back(num);
}
return v;
}
此示例代码只是将输入字符串转换为存储在vector中的一系列unsigned long long int。
在上面这种情况下,如果我有另一个函数调用这个函数,并且我们看起来在向量中有大约100,000个元素,这是否意味着,当我们返回它时,将创建一个新的向量并且将创建相同的元素返回函数中的一个,然后返回函数中的原始向量?到目前为止我的理解是否正确?
通常情况下,我会以这样的方式编写代码,使得所有函数在返回容器时都会返回指针,但是,程序设计方面,并且根据我的理解,我们应该始终返回指针来到容器?
答案 0 :(得分:8)
std::vector
很可能(如果打开了编译器优化)直接在函数的返回值中构造。这称为复制/移动省略,是允许编译器进行的优化:
在具有类返回类型的函数的return语句中,当表达式是非易失性自动对象(函数或catch子句参数除外)的名称时,具有与函数相同的cv-unqualified类型返回类型,通过将自动对象直接构造到函数的返回值
中,可以省略复制/移动操作
此引用取自C ++ 11标准,但与C ++ 03类似。重要的是要注意,复制/移动省略根本不必发生 - 这完全取决于编译器。大多数现代编译器都会处理你的例子而没有任何问题。
如果没有出现elision,C ++ 11仍将为您提供比C ++ 03更多的优势:
在C ++ 03中,没有复制省略,像这样返回std::vector
会涉及到将所有元素复制到返回的对象然后销毁本地std::vector
。 1}}。
在C ++ 11中,std::vector
移动不在函数中。移动允许返回的std::vector
窃取即将销毁的std::vector
的内容。这比复制内容要高效得多。
您可能已经预料到该对象只会被复制,因为它是一个左值,但是有一个特殊规则可以使这样的副本首先被视为移动:
当满足复制操作的省略标准并且要复制的对象由左值指定时,首先执行重载决策以选择复制的构造函数,就好像对象是由一个右值。
至于是否应该返回指向容器的指针:答案几乎肯定是否定的。你不应该传递指针,除非它是完全必要的,并且在必要时,你最好使用智能指针。正如我们所见,在你的情况下,根本没有必要,因为按价值传递它几乎没有开销。
答案 1 :(得分:4)
使用任何合理的编译器按值返回是安全的,我认为更可取。 C ++标准允许复制省略,在这种情况下为named return value optimization (NRVO),这意味着您担心的额外副本不会发生。
请注意,这是允许修改程序的可观察行为的优化案例。
注意2.正如其他答案中所提到的,C ++ 11引入了移动语义,这意味着,在RVO
不适用的情况下,您可能仍然有一个非常便宜的操作,其中的内容被返回的对象被转移到调用者。在std::vector
的情况下,这非常便宜。但请记住,并非所有类型都可以移动。
答案 2 :(得分:2)
您的理解是正确的 但编译器可以通过 RVO and NRVO 应用复制省略,并删除正在生成的额外副本。
我们是否总是在指向容器时返回指针?
如果可以,当然你应该避免重新调整值,特别是对于非POD类型。
答案 3 :(得分:2)
这取决于whether or not you need reference semantics。
一般来说,如果你不需要引用语义,我会说你不应该使用指针,因为在C ++ 11容器类中支持移动语义,所以按值返回一个集合很快。此外,编译器可以忽略对移动的构造函数的调用(这称为命名返回值优化或NRVO),因此不会引入任何开销。
但是,如果您确实需要创建集合的单独,一致的视图(即别名),那么例如在几个共享的地方“看到”返回的向量中的插入该向量的所有权,那么你应该考虑返回一个智能指针。