存储未初始化的 STL 向量?

新手上路,请多包涵

我正在编写一个内部循环,需要将 struct s 放在连续存储中。我不知道其中有多少 struct 会提前。我的问题是 STL 的 vector 将其值初始化为 0,所以无论我做什么,我都会承担初始化成本以及将 struct 的成员设置为价值观。

有什么方法可以阻止初始化,还是有一个类似 STL 的容器,具有可调整大小的连续存储和未初始化的元素?

(我确信这部分代码需要优化,而且我确信初始化是一笔不小的开销。)

另外,请参阅下面的评论以了解初始化发生的时间。

一些代码:

 void GetsCalledALot(int* data1, int* data2, int count) {
    int mvSize = memberVector.size()
    memberVector.resize(mvSize + count); // causes 0-initialization

    for (int i = 0; i < count; ++i) {
        memberVector[mvSize + i].d1 = data1[i];
        memberVector[mvSize + i].d2 = data2[i];
    }
}

原文由 Jim Hunziker 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 678
2 个回答

std::vector 必须以某种方式初始化数组中的值,这意味着必须调用一些构造函数(或复制构造函数)。 vector (或任何容器类)的行为是未定义的,如果您要访问数组的未初始化部分,就像它已初始化一样。

最好的方法是使用 reserve()push_back() ,以便使用复制构造函数,避免默认构造。

使用您的示例代码:

 struct YourData {
    int d1;
    int d2;
    YourData(int v1, int v2) : d1(v1), d2(v2) {}
};

std::vector<YourData> memberVector;

void GetsCalledALot(int* data1, int* data2, int count) {
    int mvSize = memberVector.size();

    // Does not initialize the extra elements
    memberVector.reserve(mvSize + count);

    // Note: consider using std::generate_n or std::copy instead of this loop.
    for (int i = 0; i < count; ++i) {
        // Copy construct using a temporary.
        memberVector.push_back(YourData(data1[i], data2[i]));
    }
}

像这样调用 reserve() (或 resize() )的唯一问题是,您最终可能会比您需要的更频繁地调用复制构造函数。如果你可以对数组的最终大小做出一个很好的预测,最好在开头 reserve() 空格。但是,如果您不知道最终大小,则至少平均副本数会最少。

在当前版本的 C++ 中,内部循环有点低效,因为临时值在堆栈上构造,复制构造到向量内存,最后临时值被销毁。然而,下一版本的 C++ 有一个称为 R 值引用 ( T&& ) 的功能,这将有所帮助。

std::vector 提供的接口不允许其他选项,即使用一些类似工厂的类来构造默认值以外的值。下面是这个模式在 C++ 中实现的粗略示例:

 template <typename T>
class my_vector_replacement {

    // ...

    template <typename F>
    my_vector::push_back_using_factory(F factory) {
        // ... check size of array, and resize if needed.

        // Copy construct using placement new,
        new(arrayData+end) T(factory())
        end += sizeof(T);
    }

    char* arrayData;
    size_t end; // Of initialized data in arrayData
};

// One of many possible implementations
struct MyFactory {
    MyFactory(int* p1, int* p2) : d1(p1), d2(p2) {}
    YourData operator()() const {
        return YourData(*d1,*d2);
    }
    int* d1;
    int* d2;
};

void GetsCalledALot(int* data1, int* data2, int count) {
    // ... Still will need the same call to a reserve() type function.

    // Note: consider using std::generate_n or std::copy instead of this loop.
    for (int i = 0; i < count; ++i) {
        // Copy construct using a factory
        memberVector.push_back_using_factory(MyFactory(data1+i, data2+i));
    }
}

这样做确实意味着您必须创建自己的矢量类。在这种情况下,它也使本来应该是一个简单的例子变得复杂。但是有时使用这样的工厂函数可能会更好,例如,如果插入是以其他值为条件的,那么即使实际上不需要,您也必须无条件地构造一些昂贵的临时函数。

原文由 Lloyd 发布,翻译遵循 CC BY-SA 3.0 许可协议

要澄清reserve() 响应:您需要将reserve() 与push_back() 结合使用。这样,不会为每个元素调用默认构造函数,而是调用复制构造函数。您仍然会受到在堆栈上设置结构然后将其复制到向量的惩罚。另一方面,如果您使用

vect.push_back(MyStruct(fieldValue1, fieldValue2))

编译器将直接在属于向量的内存中构造新实例。这取决于优化器的智能程度。您需要检查生成的代码以找出答案。

原文由 user3458 发布,翻译遵循 CC BY-SA 2.5 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
logo
Stack Overflow 翻译
子站问答
访问
宣传栏