【发布时间】:2020-10-07 03:10:16
【问题描述】:
我正在“现代化”一个(相当老的)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::vector不支持将向量中的构造对象放在向量中的任意位置;第一个size()元素必须全部处于活动状态(即必须已构造),其余元素(直到 capacity())不得处于活动状态(即必须未构造/必须已销毁)。原始代码如何跟踪活着的和不活着的,以便仅在非活着的对象上适当地调用析构函数? -
您有一些选择:使用
std::option。将默认 ctor 添加到my_struct。添加一个新的自定义数据结构(在原始内存上操作),其中包含“活动”元素列表(可能作为位数组)并正确管理生命周期。 -
或者代码的结构可能使得在
other_class被销毁时,对于 [0, n) 中的每个idx,foo已经被调用一次。如果是这样,则无需显式跟踪数组中的活动和不活动。这种不安全的代码可能应该严格限制在一小部分用途中。
标签: c++ vector memory-management constructor stl