C++ 带括号的取消引用(带迭代器)

C++ Dereferencing with Parenthesis (with iterators)

我的问题很简单。我有一个值向量(这里是线程,无关紧要),我想遍历它们。但是,有两个版本的代码对我来说看起来相同,但只有第二个版本有效。我想知道为什么。

版本 1 (不编译)

int main(){
    int someValue = 5;
    vector<std::thread *> threadVector;

    threadVector.resize(20);

    for (int i = 0; i < 20; i++) {
        threadVector[i] = new std::thread(foo, std::ref(someValue));
    }

    for (std::vector<std::thread *>::iterator it = threadVector.begin(); it != threadVector.end(); ++it) {
        *it->join(); // *********Notice this Line*********
    }

    system("pause"); // I know I shouldn't be using this
}

版本 2 (有效)

int main(){
    int someValue = 5;
    vector<std::thread *> threadVector;

    threadVector.resize(20);

    for (int i = 0; i < 20; i++) {
        threadVector[i] = new std::thread(foo, std::ref(someValue));
    }

    for (std::vector<std::thread *>::iterator it = threadVector.begin(); it != threadVector.end(); ++it) {
        (*it)->join(); // *********Notice this Line*********
    }

    system("pause"); // I know I shouldn't be using this
}

这是操作顺序的问题。

*it->join();

解析为:

*(it->join());

作为挑战,我第一次接触了 C++11。我发现您可以在不动态分配 std::thread 对象的情况下实现相同的目的:

#include <iostream>
#include <thread>
#include <vector>

void function()
{
    std::cout << "thread function\n";
}

int main()
{
    std::vector<std::thread> ths;
    ths.push_back(std::move(std::thread(&function)));
    ths.push_back(std::move(std::thread(&function)));
    ths.push_back(std::move(std::thread(&function)));

    while (!ths.empty()) {
        std::thread th = std::move(ths.back());
        ths.pop_back();
        th.join();
    }
}

这是可行的,因为 std::thread 有一个构造函数和赋值运算符采用右值引用,使其可移动。此外,所有容器都支持存储可移动对象,并且它们在重新分配时移动而不是复制它们。阅读一些关于这个新的 C++11 功能的在线文章,它太宽泛在这里无法解释,我也不太了解它。

关于您提出的线程具有内存成本的问题,我认为您的方法不是优化。相反,动态分配本身在内存和性能方面都有开销。对于小对象,一个或两个指针加上可能的一些填充的内存开销是巨大的。如果 std::thread 对象只有单个指针的大小,我不会感到惊讶,给你超过 100% 的开销。

请注意,这仅涉及 std::thread 对象。实际线程所需的内存,特别是它的堆栈,是一个不同的问题。但是,std::thread 对象和实际线程没有 1:1 关系,std::thread 对象的动态分配也不会改变任何东西。

如果你还是怕重新分配太贵,可以提前预留一个合适的size,避免重新分配。但是,如果这确实是一个问题,那么您创建和终止线程的方式太多了,这将使移动一些小对象的开销相形见绌。考虑使用线程池。