1

我需要弄清楚这一点。使用下面的代码:

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;
}

此示例代码只是将输入字符串转换为存储在向量中的一系列 unsigned long long int。

在上面的这种情况下,如果我有另一个函数调用这个函数,并且我们在向量中似乎有大约 100,000 个元素,这是否意味着,当我们返回它时,将创建一个新向量并且将创建与该向量相同的元素在函数中,然后返回时会消除函数中的原始向量?到目前为止我的理解正确吗?

通常,我会以这样一种方式编写代码,即所有函数在涉及到容器时都会返回指针,但是,在程序设计方面,根据我上面的理解,我们是否应该在涉及到容器时总是返回一个指针

4

4 回答 4

8

std::vector有可能(如果您的编译器优化已打开)直接在函数的返回值中构造。这称为复制/移动省略,是允许编译器进行的优化:

在具有类返回类型的函数的 return 语句中,当表达式是具有与函数返回类型相同的 cv 非限定类型的非易失性自动对象(函数或 catch 子句参数除外)的名称时,通过将自动对象直接构造到函数的返回值中,可以省略复制/移动操作

此引用取自 C++11 标准,但与 C++03 类似。重要的是要注意复制/移动省略根本不需要发生 - 这完全取决于编译器。大多数现代编译器将毫无问题地处理您的示例。

如果没有发生省略,C++11 仍然会为您提供比 C++03 更多的好处:

  • 在 C++03 中,如果没有复制省略,返回std::vector这样的 a 将涉及,如您所说,将所有元素复制到返回的对象,然后销毁本地std::vector.

  • 在 C++11 中,std::vector将被移出函数。移动允许返回std::vector的人窃取std::vector即将被销毁的内容。这比复制内容效率高得多。

    你可能已经预料到对象会被复制,因为它是一个左值,但是有一个特殊的规则使这样的复制首先被视为移动:

    当满足省略复制操作的标准 [...] 并且要复制的对象由左值指定时,首先执行为复制选择构造函数的重载决策,就好像对象由右值指定一样。

至于是否应该返回指向容器的指针:答案几乎肯定是否定的。除非完全必要,否则不应传递指针,并且在必要时,最好使用智能指针。正如我们所看到的,在您的情况下,根本没有必要,因为按值传递它几乎没有开销。

于 2013-02-09T15:37:43.457 回答
4

使用任何合理的编译器按值返回是安全的,我会说更可取。C++ 标准允许复制省略,在本例中称为返回值优化(NRVO),这意味着您担心的额外复制不会发生。

请注意,这是允许修改程序的可观察行为的优化案例。

注意 2. 正如其他答案中提到的,C++11 引入了移动语义,这意味着,在RVO不适用的情况下,您可能仍然有一个非常便宜的操作,将返回的对象的内容转移到呼叫者,召集者。在 的情况下std::vector,这是非常便宜的。但请记住,并非所有类型都可以移动。

于 2013-02-09T15:36:09.440 回答
2

你的理解是正确的。
但是编译器可以通过RVO 和 NRVO应用复制省略并删除正在生成的额外副本。

当涉及到容器时,我们是否应该总是返回一个指针?

如果可以,当然应该避免按值返回,尤其是对于非 POD 类型。

于 2013-02-09T15:35:50.930 回答
2

这取决于您是否需要引用语义

一般来说,如果你不需要引用语义,我会说你不应该使用指针,因为在 C++11 中容器类支持移动语义,所以按值返回集合很快。此外,编译器可以省略对移动构造函数的调用(这称为命名返回值优化或 NRVO),因此不会引入任何开销。

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

于 2013-02-09T15:36:14.690 回答