保留后直接分配给 std::vector 不会引发错误但不会增加向量大小
Directly assigning to a std::vector after reserving does not throw error but does not increase vector size
让我们创建一个助手 class 来帮助可视化问题:
class C
{
int ID = 0;
public:
C(const int newID)
{
ID = newID;
}
int getID()
{
return ID;
}
};
假设您创建一个空的 std::vector<C>
,然后保留它以容纳 10 个元素:
std::vector<C> pack;
pack.reserve(10);
printf("pack has %i\n", pack.size()); //will print '0'
现在,您将 C
的新实例分配到向量的索引 4 中:
pack[4] = C(57);
printf("%i\n", pack[4].getID()); //will print '57'
printf("pack has %i\n", pack.size()); //will still print '0'
我发现有两点很奇怪:
1) 即使在发布模式下,赋值也不应该使编译器(Visual Studio 2015,发布模式)抛出错误吗?
2) 因为它没有,而且元素实际上存储在位置 4,那么向量不应该有 size = 1 而不是零吗?
未定义的行为仍未定义。如果我们将其设为对象向量,您会更清楚地看到意外行为。
#include <iostream>
#include <vector>
struct Foo {
int data_ = 3;
};
int main() {
std::vector<Foo> foos;
foos.reserve(10);
std::cout << foos[4].data_; // This probably doesn't output 3.
}
这里可以看到,因为我们还没有真正分配对象,所以构造函数还没有运行。
另一个例子,因为你正在使用 space 向量实际上还没有开始分配给你,如果向量需要重新分配它的后备内存,你写的值将不会被复制.
#include <iostream>
#include <vector>
int main() {
std::vector<int> foos;
foos.reserve(10);
foos[4] = 100;
foos.reserve(10000000);
std::cout << foos[4]; // Probably doesn't print 100.
}
简答:
1) 没有理由抛出异常,因为 operator[]
不应该验证您已经通过的位置。它可能会在 Debug 模式下这样做,但肯定不会在 Release 模式下这样做(否则性能会受到影响)。在 Release 模式下,编译器相信您提供的代码是防错的,并且会尽一切努力使您的代码更快。
Returns a reference to the element at specified location pos. No
bounds checking is performed.
http://en.cppreference.com/w/cpp/container/vector/operator_at
2) 你只是访问了你还不拥有的内存(reserve
不是 resize
),你对它所做的任何事情都是未定义的行为。但是,您从未将元素添加到 vector
中,它甚至不知道您修改了它的缓冲区。正如@Bill 所展示的,vector
可以在不复制本地更改的情况下更改其缓冲区。
编辑:
此外,如果使用 vector::at
函数,可能会由于边界检查而出现异常。
即:pack.at(4) = C(57);
抛出异常
让我们创建一个助手 class 来帮助可视化问题:
class C
{
int ID = 0;
public:
C(const int newID)
{
ID = newID;
}
int getID()
{
return ID;
}
};
假设您创建一个空的 std::vector<C>
,然后保留它以容纳 10 个元素:
std::vector<C> pack;
pack.reserve(10);
printf("pack has %i\n", pack.size()); //will print '0'
现在,您将 C
的新实例分配到向量的索引 4 中:
pack[4] = C(57);
printf("%i\n", pack[4].getID()); //will print '57'
printf("pack has %i\n", pack.size()); //will still print '0'
我发现有两点很奇怪:
1) 即使在发布模式下,赋值也不应该使编译器(Visual Studio 2015,发布模式)抛出错误吗?
2) 因为它没有,而且元素实际上存储在位置 4,那么向量不应该有 size = 1 而不是零吗?
未定义的行为仍未定义。如果我们将其设为对象向量,您会更清楚地看到意外行为。
#include <iostream>
#include <vector>
struct Foo {
int data_ = 3;
};
int main() {
std::vector<Foo> foos;
foos.reserve(10);
std::cout << foos[4].data_; // This probably doesn't output 3.
}
这里可以看到,因为我们还没有真正分配对象,所以构造函数还没有运行。
另一个例子,因为你正在使用 space 向量实际上还没有开始分配给你,如果向量需要重新分配它的后备内存,你写的值将不会被复制.
#include <iostream>
#include <vector>
int main() {
std::vector<int> foos;
foos.reserve(10);
foos[4] = 100;
foos.reserve(10000000);
std::cout << foos[4]; // Probably doesn't print 100.
}
简答:
1) 没有理由抛出异常,因为 operator[]
不应该验证您已经通过的位置。它可能会在 Debug 模式下这样做,但肯定不会在 Release 模式下这样做(否则性能会受到影响)。在 Release 模式下,编译器相信您提供的代码是防错的,并且会尽一切努力使您的代码更快。
Returns a reference to the element at specified location pos. No bounds checking is performed.
http://en.cppreference.com/w/cpp/container/vector/operator_at
2) 你只是访问了你还不拥有的内存(reserve
不是 resize
),你对它所做的任何事情都是未定义的行为。但是,您从未将元素添加到 vector
中,它甚至不知道您修改了它的缓冲区。正如@Bill 所展示的,vector
可以在不复制本地更改的情况下更改其缓冲区。
编辑:
此外,如果使用 vector::at
函数,可能会由于边界检查而出现异常。
即:pack.at(4) = C(57);
抛出异常