16

考虑:

#include <cstdlib>
#include <memory>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;

class Gizmo
{
public:
    Gizmo() : foo_(shared_ptr<string>(new string("bar"))) {};
    Gizmo(Gizmo&& rhs); // Implemented Below

private:
    shared_ptr<string> foo_;
};

/*
// doesn't use std::move
Gizmo::Gizmo(Gizmo&& rhs)
:   foo_(rhs.foo_)
{
}
*/


// Does use std::move
Gizmo::Gizmo(Gizmo&& rhs)
:   foo_(std::move(rhs.foo_))
{
}

int main()
{
    typedef vector<Gizmo> Gizmos;
    Gizmos gizmos;
    generate_n(back_inserter(gizmos), 10000, []() -> Gizmo
    {
        Gizmo ret;
        return ret;
    });

    random_shuffle(gizmos.begin(), gizmos.end());

}

在上面的代码中,有两个版本Gizmo::Gizmo(Gizmo&&)——一个用于std::move实际移动shared_ptr另一个只是复制shared_ptr.

这两个版本似乎都在表面上起作用。一个区别(我能看到的唯一区别)是在非move版本中,引用计数shared_ptr暂时增加,但只是短暂增加。

我通常会继续move使用shared_ptr,但只是为了在我的代码中保持清晰和一致。我在这里错过了一个考虑吗?出于任何技术原因,我应该更喜欢一个版本吗?

4

3 回答 3

19

这里的主要问题不是由于额外的原子递增和递减导致的性能差异很小,shared_ptr而是操作的语义不一致,除非您执行移动。

虽然假设 的引用计数shared_ptr只是暂时的,但语言中没有这样的保证。您要从中移动的源对象可以是临时的,但它也可能具有更长的生命周期。它可能是一个已被强制转换为右值引用(例如std::move(var))的命名变量,在这种情况下,通过不从移动shared_ptr源保持共享所有权,并且如果目标shared_ptr的范围更小,那么指向对象的生命周期将不必要地延长。

于 2012-06-08T17:47:57.110 回答
15

我赞成 James McNellis 的回答。我想对他的回答发表评论,但我的评论不符合评论格式。所以我把它放在这里。

衡量移动shared_ptr与复制对性能影响的一种有趣方法是使用诸如vector<shared_ptr<T>>移动或复制一大堆它们并计时之类的东西。大多数编译器都可以通过指定语言模式来打开/关闭移动语义(例如 -std=c++03 或 -std=c++11)。

这是我刚刚在 -O3 测试的代码:

#include <chrono>
#include <memory>
#include <vector>
#include <iostream>

int main()
{
    std::vector<std::shared_ptr<int> > v(10000, std::shared_ptr<int>(new int(3)));
    typedef std::chrono::high_resolution_clock Clock;
    typedef Clock::time_point time_point;
    typedef std::chrono::duration<double, std::micro> us;
    time_point t0 = Clock::now();
    v.erase(v.begin());
    time_point t1 = Clock::now();
    std::cout << us(t1-t0).count() << "\u00B5s\n";
}

使用 clang/libc++ 并在 -std=c++03 中为我打印出来:

195.368µs

切换到 -std=c++11 我得到:

16.422µs

你的旅费可能会改变。

于 2012-06-08T18:12:43.903 回答
13

最好使用 of move:它应该比副本更有效,因为它不需要引用计数的额外原子递增和递减。

于 2012-06-08T17:26:34.060 回答