关于在C ++中返回容器:指针VS非指针

时间:2013-02-09 15:32:50

标签: c++ function return containers

我需要直截了当。使用下面的代码:

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个元素,这是否意味着,当我们返回它时,将创建一个新的向量并且将创建相同的元素返回函数中的一个,然后返回函数中的原始向量?到目前为止我的理解是否正确?

通常情况下,我会以这样的方式编写代码,使得所有函数在返回容器时都会返回指针,但是,程序设计方面,并且根据我的理解,我们应该始终返回指针来到容器

4 个答案:

答案 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),因此不会引入任何开销。

但是,如果您确实需要创建集合的单独,一致的视图(即别名),那么例如在几个共享的地方“看到”返回的向量中的插入该向量的所有权,那么你应该考虑返回一个智能指针。