根据,用值的副本std::vector::assign
替换内容。此定义意味着向量的现有状态未在中使用。count
value
assign
上运行的以下示例代码中,test1
打印20000002
。这表明在分配新空间后删除了现有内存。通过合理放置clear()
&shrink_to_fit()
调用,test2
可以减少峰值对象数并打印10000002
。
这是实现过程中错失的优化机会libstdc++
还是我对std::vector
功能的理解不正确/不完整?
#include <iostream>
#include <vector>
int curr;
int max;
void update(int delta) { curr += delta; max = std::max(max, curr); }
void reset() { curr = max = 0; }
struct Foo {
Foo() { update(1); }
Foo(const Foo&) { update(1); }
Foo& operator=(const Foo&) { return *this; }
~Foo() { update(-1); }
};
void test1()
{
reset();
std::vector<Foo> foos;
foos.assign(10'000'000, Foo{});
foos.assign(10'000'001, Foo{});
std::cout << max << '\n';
}
void test2()
{
reset();
std::vector<Foo> foos;
foos.assign(10'000'000, Foo{});
foos.clear();
foos.shrink_to_fit();
foos.assign(10'000'001, Foo{});
std::cout << max << '\n';
}
int main()
{
test1();
test2();
return 0;
}
12
最佳答案
1
观察到的 20mil 意味着有那么一刻,第二个assign
对象创建了,但第一个对象assign
尚未被删除。
事实上,第二个assign
更大。因此,如果 vector 为第一个分配了精确数量的内存,那么它应该为第二个创建另一块内存。它将在其中创建第二个对象加载,将值增加到20 mil。assign
assign
max
然后它释放为前 1000 万个元素分配的内存。
它不会提前释放第一个内存块,因为在构造第二个内存块时可能会发生异常。所以只有在保证第二个内存块分配和填充成功的情况下,才会释放第一个内存块。
您的问题是正确的:assign
没有提供强有力的异常保证,即,如果第二个assign
不需要重新分配,那么很可能不会重新分配。
但如果重新分配不可避免,他们会选择提供强有力的担保。
以下是对数字发生的情况的解释:
void test1()
{
reset();
std::vector<Foo> foos;
foos.assign(10'000'000, Foo{});
// allocated underlying memory for first bunch of elements
// constructed first 10mil elements
foos.assign(10'000'001, Foo{});
// allocated underlying memory for second bunch of elements
// constructed second 10mil elements,
// max reached 20mil
//then released first 10mil elements and their underlying memory
std::cout << max << '\n';
}
void test2()
{
reset();
std::vector<Foo> foos;
foos.assign(10'000'000, Foo{}); // constructed first 10 mil elements, `curr` == 10mil
foos.clear(); // deleted elements, reducing `curr` to 0.
foos.shrink_to_fit(); // released underlying memory
foos.assign(10'000'001, Foo{});
// allocated underlying memory for 10mil+1 elements
// constructed those elements, `curr` modified from 0 to 10mil+1
std::cout << max << '\n'; // prints 10mil+1
}
3
-
第一组将在第二组成功构建后销毁。因为如果在第二组构建过程中发生异常,第一组应该保持不变。
– -
哦没关系,我误解了楼主的代码。这一切都说得通。
–
-
也许你可以测试一下如果不可移动会发生什么
Foo{}
,因为这样就没有必要为了异常安全而创建两个单独的巨大分配。
–
|
shrink_to_fit()
。–
–
–
–
noexcept
示例代码不会改变答案。当然,new
可能会引发异常,这意味着至少在新分配完成之前,删除现有内存是不安全的。–
|