【问题标题】:Regarding returning containers in C++: pointer VS non-pointer关于在 C++ 中返回容器:指针 VS 非指针
【发布时间】:2013-02-09 15:32:49
【问题描述】:

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

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

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

【问题讨论】:

  • “当涉及到容器时,我们是否应该总是返回一个指针?” - 绝对不是。无论如何不要仅仅因为担心效率而引入动态分配,在 C++11 中更是如此。您是否需要动态生命周期。即使在 C++03 中,如果您不需要动态分配,引用参数也比返回动态分配的对象要好得多,而在 C++11 中,整个讨论仍然没有实际意义。

标签: c++ function return containers


【解决方案1】:

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

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

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

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

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

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

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

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

【讨论】:

  • 复制省略将胜过 C++11 中的举措。
  • @juanchopanza 我说过! :P
  • +1 - 我很震惊这么多答案都没有提到移动语义,只专注于 RVO 的有效但实现定义的参数。
  • @ChristianRau:因为在这种情况下,即使在 C++11 中,复制省略也会消除复制或移动的需要。
  • @ChristianRau 有人可能会争辩说,移动语义只会混淆这个问题。另外,移动需要可移动的类型(当然,std::vector 是其中之一)。
【解决方案2】:

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

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

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

【讨论】:

    【解决方案3】:

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

    我们应该总是返回一个指向容器的指针吗?

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

    【讨论】:

      【解决方案4】:

      这取决于whether or not you need reference semantics

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

      但是,如果您确实需要为您的集合创建单独的、一致的视图(即 别名),以便例如插入到返回的向量中的内容将在多个共享拥有该向量的所有权,那么您应该考虑返回一个智能指针。

      【讨论】:

        猜你喜欢
        • 2018-05-30
        • 1970-01-01
        • 1970-01-01
        • 2017-05-13
        • 2019-12-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-03
        相关资源
        最近更新 更多