5

我正在试验std::async并最终得到一个看起来像这样的代码:

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

4

2 回答 2

7

在您的情况下,obj正在复制:

  1. 两次调用std::async
  2. 两次 byasync的内部调用std::bind
  3. 一次通过调用,void foo(obj a)因为它a按值取值。

信不信由你,自 VC10 以来,副本的数量实际上已经减少了。

看到一个库(无论是标准库还是另一个库)触发的副本比您对您的类型所期望的要多一些,这种情况并不少见。通常,您对此无能为力。

人们通常会做两件事来防止复制:

  1. obj通过引用(或者在你的情况下, const ref 因为不foo修改obj)。这将需要使用std::ref异步。
  2. 为定义一个移动构造函数obj。这不会阻止构建和销毁临时对象,但它会让您有机会稍微优化流程。

请注意,在您仅保留一个对象的裸例中,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;
    }

    /* ... */
};
于 2013-02-16T17:27:37.430 回答
2

a在绑定阶段被复制。为避免 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));
    ...

这样,仍然会创建 5 个 obj 实例,但由于异步支持movable对象,相同的副本将从一个实例移动到另一个实例(对于重的对象,这将比复制对象更重要)。所以现在输出应该是:

new obj
move obj
move obj
move obj
move obj
delete obj
delete obj
delete obj
5
delete obj
delete obj
于 2013-02-16T16:42:45.310 回答