我正在"现代化"一个(相当古老的(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);
}
};
我将arr
更改为std::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);
}
};
如何使这段代码快速安全(假设我无法向my_struct
添加默认 ctor (?谢谢。
编辑:这是示例代码。它按预期编译和运行,没有任何警告:http://cpp.sh/8ytwf
但我知道这不行,因为 std::reserve 不会增加该向量的大小
std::reserve
确实增加了向量容量,当您只想分配内存并且稍后才推送元素时,这正是您想要的。
向量管理的基础数组的大小不等于向量size()
。size()
返回容器中的元素数,只要没有任何size() == 0
。
当你稍后推送元素时,你不需要使用放置new,但你应该使用push_back
或emplace_back
。更具体地说,这是错误的:
new (&arr[idx]) my_struct(a, b, c);
因为您正在越界访问向量(请记住:大小是元素的数量,容量可以更大,但您不能访问大于向量大小的索引(。相反:
arr.emplace_back(a,b,c);
虽然我更喜欢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 );
}
};