如何正确使用 std::vector.back() ?
How to use `std::vector.back()` correctly?
我的代码中有一个我不太明白的错误。根据文档,std::vector.back()
returns 对容器中最后一个元素的引用,所以这就是我所做的:(live here)
#include <iostream>
#include <vector>
class Foo {
public:
Foo(int id) : id(id) {
std::cout << "foo " << id << " constructed" << std::endl;
}
~Foo() {
std::cout << "foo " << id << " destructed" << std::endl;
}
int id;
};
int main() {
std::vector<Foo> foos;
for (int i = 0; i < 2; i++) {
foos.emplace_back(i); // construct foo in place
auto& foo = foos.back();
std::cout << "Play with " << foo.id << std::endl;
}
for (auto&& foo : foos) {
std::cout << "I'm foo " << foo.id << std::endl;
}
return 0;
}
产生以下输出:
foo 0 constructed
Play with 0
foo 1 constructed
foo 0 destructed
Play with 1
I'm foo 0
I'm foo 1
foo 0 destructed
foo 1 destructed
那么,在 Foo
的第二个实例构造之后,第一个被销毁了,为什么?因为 auto& foo
超出范围?但是当我打印 foos
向量时,它仍然有两个 foos,真令人惊讶!乍一看似乎 foos.back()
正在复制,但事实并非如此。实在想不明白的是,怎么可能构造函数被调用了两次,而析构函数却被调用了3次,他们不应该总是成对出现吗?
这里发生的事情是,当你 .emplace_back(1)
时 std::vector
没有足够的 space 用于两个元素,所以它必须重新分配, copy/move 所有现有的元素到新的分配,然后 emplace_back
新的。因此,您看到唯一的现有元素从旧分配中被破坏。
如果你在循环之前.reserve(2)
,这就会消失:https://godbolt.org/z/oj8fWhznP
foo 0 constructed
Play with 0
foo 1 constructed
Play with 1
I'm foo 0
I'm foo 1
foo 0 destructed
foo 1 destructed
也可以用.capacity()
查看分配的大小,确实验证了理论:https://godbolt.org/z/a88xfqa7f
--- 0 ---
capacity: 0
foo 0 constructed
capacity: 1
Play with 0
--- 1 ---
capacity: 1
foo 1 constructed
foo 0 destructed
capacity: 2
Play with 1
为 move/copy 构造函数和赋值运算符添加日志记录定义可能会让您更好地了解 std::vector
在内部做什么。
我的代码中有一个我不太明白的错误。根据文档,std::vector.back()
returns 对容器中最后一个元素的引用,所以这就是我所做的:(live here)
#include <iostream>
#include <vector>
class Foo {
public:
Foo(int id) : id(id) {
std::cout << "foo " << id << " constructed" << std::endl;
}
~Foo() {
std::cout << "foo " << id << " destructed" << std::endl;
}
int id;
};
int main() {
std::vector<Foo> foos;
for (int i = 0; i < 2; i++) {
foos.emplace_back(i); // construct foo in place
auto& foo = foos.back();
std::cout << "Play with " << foo.id << std::endl;
}
for (auto&& foo : foos) {
std::cout << "I'm foo " << foo.id << std::endl;
}
return 0;
}
产生以下输出:
foo 0 constructed
Play with 0
foo 1 constructed
foo 0 destructed
Play with 1
I'm foo 0
I'm foo 1
foo 0 destructed
foo 1 destructed
那么,在 Foo
的第二个实例构造之后,第一个被销毁了,为什么?因为 auto& foo
超出范围?但是当我打印 foos
向量时,它仍然有两个 foos,真令人惊讶!乍一看似乎 foos.back()
正在复制,但事实并非如此。实在想不明白的是,怎么可能构造函数被调用了两次,而析构函数却被调用了3次,他们不应该总是成对出现吗?
这里发生的事情是,当你 .emplace_back(1)
时 std::vector
没有足够的 space 用于两个元素,所以它必须重新分配, copy/move 所有现有的元素到新的分配,然后 emplace_back
新的。因此,您看到唯一的现有元素从旧分配中被破坏。
如果你在循环之前.reserve(2)
,这就会消失:https://godbolt.org/z/oj8fWhznP
foo 0 constructed
Play with 0
foo 1 constructed
Play with 1
I'm foo 0
I'm foo 1
foo 0 destructed
foo 1 destructed
也可以用.capacity()
查看分配的大小,确实验证了理论:https://godbolt.org/z/a88xfqa7f
--- 0 ---
capacity: 0
foo 0 constructed
capacity: 1
Play with 0
--- 1 ---
capacity: 1
foo 1 constructed
foo 0 destructed
capacity: 2
Play with 1
为 move/copy 构造函数和赋值运算符添加日志记录定义可能会让您更好地了解 std::vector
在内部做什么。