2013-02-16 51 views
4

我与std::async实验和结束了,看起来像一个代码:的std ::异步和对象复制

class obj { 
public: 
    int val; 

    obj(int a) : val(a) { 
     cout << "new obj" << endl; 
    } 
    ~obj() { 
     cout << "delete obj" << endl; 
    } 
}; 


void foo(obj a) { 

    this_thread::sleep_for(chrono::milliseconds(500)); 
    cout << a.val << endl; 
} 

int main(int argc, int **args) { 

    obj a(5); 
    auto future = async(foo, a); 
    future.wait(); 

    return 0; 
} 

,其结果是:

new obj 
delete obj 
delete obj 
delete obj 
5 
delete obj 
delete obj 
delete obj 

然后我试图改变void foo(obj a)通过void foo(obj &a)

new obj 
delete obj 
delete obj 
delete obj 
5 
delete obj 
delete obj 

我对象的5个人副本为何会为这个简单的代码? 我不得不承认,我真的很困惑。有人会关心解释这一点吗?

编辑

我使用VS2012

+1

尝试进行复制或移动构造'obj'类中的uctor。 – 2013-02-16 15:58:22

+0

虽然他看到复制构造函数的效果,但知道_why_还有5个副本仍然很有趣。遵循代码的我简单的头脑可以看到三个(也许是四个)明显的,但至少有两个并不明显。 – Chad 2013-02-16 16:04:54

+1

在GCC 4.7上使用'const obj&'作为'foo'的参数,我得到3个“delete obj”,这意味着正在创建2个副本。 – mfontanini 2013-02-16 16:23:09

回答

5

在你的情况,obj被复制:

  1. 两次调用std::async
  2. 两次async的内部电话std::bind
  3. 曾经致电void foo(obj a),因为它的价值为a

不管你信不信,副本数量实际上是reduced since VC10

看到一个图书馆(不管是标准图书馆还是另一个图书馆)会触发比您期望的更多的副本,这种情况并不罕见。通常,没有太多你可以做些什么

有两件事情,人们通常做些什么来防止拷贝。

  1. 以供参考obj(或你的情况,const的裁判因为foo不修改obj)。这将需要使用std::ref与异步。
  2. 定义objmove constructor。这不会妨碍临时建造和销毁,但它会给你一个优化过程的机会。

请注意,在仅保留一个对象int的对象的裸示例中,复制而不是移动或通过引用传递实际上可能会更快。


用于使obj通过引用到async实施例:

void foo(const obj& a) { 
    this_thread::sleep_for(chrono::milliseconds(500)); 
    cout << a.val << endl; 
} 

int main(int argc, int **args) { 
    obj a(5); 
    auto future = async(foo, std::cref(a)); 
    future.wait(); 

    return 0; 
} 

实施例,用于定义一个转移构造:

class obj 
{ 
public: 
    /* ... */ 

    obj(obj&& a) : val(move(a.val)) { 
     // It is good practice to 0 out the moved object to catch use-after-move bugs sooner. 
     a.val = 0; 
    } 

    /* ... */ 
}; 
+0

这里描述http://stackoverflow.com/a/15040860/893819我认为声明一个移动构造函数实际上可以阻止临时创建。 – odinthenerd 2013-02-23 13:25:57

+0

@PorkyBrain,这不是答案所说的。编译器也可以删除副本,但不受移动辅助构件存在的控制 – 2013-02-23 14:03:45

1

a在绑定阶段被复制。要避免的多个副本,使用move constructor语义:

添加move ctorobj

class obj { 
public: 
    ... 
    obj(obj&& other) { 
     cout << "move obj" << endl; 
     val = std::move(other.val); 
    } 
}; 

在主:

obj a(5); 
    auto future = async(foo, std::move(a)); 
    ... 

这样,obj的5个实例仍然会被创建,但由于异步支持movable对象,相同的副本将从实例移动到实例(对于重物而言,这将对复制周围的物体显着)。所以,现在的输出应该是:

new obj 
move obj 
move obj 
move obj 
move obj 
delete obj 
delete obj 
delete obj 
5 
delete obj 
delete obj