当前位置:  开发笔记 > 编程语言 > 正文

具有未初始化存储的STL载体?

如何解决《具有未初始化存储的STL载体?》经验,为你挑选了4个好方法。

我正在编写一个内部循环,需要将structs放在连续的存储中.我不知道有多少这些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];
    }
}

Lloyd.. 24

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

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

使用您的示例代码:

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

std::vector 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-Value references(T&&)的功能,这将有所帮助.

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

template 
class my_vector_replacement {

    // ...

    template 
    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));
    }
}

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



1> Lloyd..:

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

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

使用您的示例代码:

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

std::vector 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-Value references(T&&)的功能,这将有所帮助.

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

template 
class my_vector_replacement {

    // ...

    template 
    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));
    }
}

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



2> fredoverflow..:

的C++ 0x增加了新的成员函数模板emplace_backvector(这依赖于可变参数模板和完善的转发)是摆脱完全的任何临时对象:

memberVector.emplace_back(data1[i], data2[i]);



3> 小智..:

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

vect.push_back(MyStruct(fieldValue1, fieldValue2))

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


事实证明,在O3级别,gcc的优化器不够智能,无法避免复制.

4> goertzenator..:

在C++ 11(和boost)中,您可以使用数组版本unique_ptr来分配未初始化的数组.这不是一个stl容器,但仍然是内存管理和C++ - ish,这对于许多应用程序来说已经足够了.

auto my_uninit_array = std::unique_ptr(new mystruct[count]);

推荐阅读
携手相约幸福
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有