如何为std::vector分配内存,然后再为一些元素调用构造函数?

问题描述 投票:0回答:1

我正在对一个(相当老的)C++项目进行 "现代化 "改造,偶然发现了这个部分。

旧的代码是为一个动态数组分配内存,然后在需要的时候为元素调用构造函数。我想为所有元素调用构造函数的成本很高,所以作者选择了这种方式(性能对这个项目至关重要)。旧的代码是这样的(简化了)。

struct my_struct {
    my_struct(int x, int y, int z) { /* expensive ctor */ }
};

struct other_class {
    my_struct* arr;
    other_class(int n) {        
        arr = (my_struct*) malloc(n * sizeof(arr[0]);
    }

    void foo(int idx, int a, int b, int c) {
        new (&arr[idx]) my_struct(a, b, c);
    }
};

我改了 arrstd::vector<my_struct>并使用 std::reserve 来 "保留 "内存。代码工作正常,通过了当前所有的测试,但我知道这是不对的,因为 std::reserve 并不会增加该向量的大小,所以调用 arr.size() 仍然会返回0。这是我的代码。

struct other_class {
    std::vector<my_struct> arr;
    other_class(int n) {        
        arr.reserve(n);
    }

    void foo(int idx, int a, int b, int c) {
        new (&arr[idx]) my_struct(a, b, c);
    }
};

如何让这段代码又快又安全(假设我不能在我的代码中加入一个默认的ctor my_struct)? 谢谢你。


编辑:这里是示例代码。它被编译并按预期运行,没有任何警告。http: /cpp.sh8ytwf

c++ vector memory-management constructor stl
1个回答
6
投票

但我知道这是不对的,因为std::reserve并没有增加那个向量的大小。

std::reserve 确实增加了向量的容量,当你只想分配内存,只想以后推送元素时,这正是你想要的。

向量所管理的底层数组的大小不等于向量的 size(). size() 返回容器中的元素数量,只要没有任何的 size() == 0.

当你以后推送元素时,你不需要使用新的位置,但你应该使用 push_backemplace_back. 更具体的说,这是错误的,因为你访问的是界外的向量(记住:size是元素数,容量可以更大,但不能访问比向量大小更大的索引)。

 new (&arr[idx]) my_struct(a, b, c);

因为你访问的是越界的向量(记住: 大小是元素的数量,容量可以更大,但你不能访问比向量大小更大的索引)。相反的,我们可以用 "向量 "来代替。

 arr.emplace_back(a,b,c);

2
投票

虽然我喜欢 emplace_back 更好的解决方案,你可以使用 std::optional 在这种情况下。这将允许您使用 idx 来决定哪个元素被有效对象填充。

struct other_class 
{
    std::vector<std::optional<my_struct>> arr;
    other_class( int n ) : arr( n, std::nullopt )
    { }

    void foo( int idx, int a, int b, int c ) 
    {
        arr.at( idx ).emplace( a, b, c );
    }
};
© www.soinside.com 2019 - 2024. All rights reserved.