我想使用std :: string简单地创建动态缓冲区,而不是使用索引迭代它。 resize()是实际分配缓冲区的唯一函数吗?
我尝试了reserve()但是当我尝试通过索引访问字符串时它断言。此外,当字符串的默认容量似乎是15个字节(在我的情况下)但如果我仍然无法以my_string[1]
访问它。
那么字符串的容量不是实际的缓冲区? reserve()也不会分配实际的缓冲区吗?
string my_string;
// I want my string to have 20 bytes long buffer
my_string.reserve( 20 );
int i = 0;
for ( parsing_something_else_loop )
{
char ch = <business_logic>;
// store the character in
my_string[i++] = ch; // this crashes
}
如果我执行resize()而不是reserve(),那么它可以正常工作。字符串是如何容量但却无法使用[]真正访问它?是不是要保留()大小以便你可以访问它?
添加-上 在回答这个问题时,我想问问一下这些人,为什么有人会在resize()完全相同的时候使用reserve()并且还会初始化字符串?我不得不说我不太欣赏这种情况下的表现论点。所有resize()除了reserve()之外还做了更多的事情,它只是初始化缓冲区,我们知道它总是很好。我们可以在岛上投票保留()吗?
答案 0 :(得分:33)
是不是要保留()大小以便你可以访问它?
不,这就是resize()
。
reserve()
仅提供足够的空间,以便将来调用导致大小增加(例如调用push_back()
)将更有效。
从您的用例看,您应该使用.push_back()
代替。
my_string.reserve( 20 );
for ( parsing_something_else_loop )
{
char ch = <business_logic>;
my_string.push_back(ch);
}
字符串是如何容量但却无法使用[]?
真正访问它
打电话给.reserve()
就好像吹山一样给你一些自由的土地。自由土地的数量是.capacity()
。土地在那里,但这并不意味着你可以住在那里。您必须建造房屋才能入住。房屋数量为.size()
(= .length()
)。
假设您正在建造一座城市,但在建造第50座城堡之后您发现没有足够的土地,因此您需要找到另一个足够大的地方以适应第51间房屋,然后将整个人口迁移到那里。这是非常低效的。如果您知道需要预先建造1000个房屋,那么您可以致电
my_string.reserve(1000);
获得足够的土地建造1000所房屋,然后你打电话
my_string.push_back(ch);
建造房屋,并将ch
分配到此位置。容量是1000,但大小仍然是1.你可能不会说
my_string[16] = 'c';
因为房子#16还不存在。你可以打电话
my_string.resize(20);
将房屋#0~#19一次性建成,这就是为什么
my_string[i++] = ch;
工作正常(只要0≤ i ≤19)。
另见http://en.wikipedia.org/wiki/Dynamic_array。
有关您的附加问题,
.resize()
无法完全取代.reserve()
,因为(1)您并不总是需要用尽所有已分配的空格,并且(2)默认构造+复制赋值是一个两步过程,这可能比直接构建(特别是对于大型对象)需要更多的时间,即
#include <vector>
#include <unistd.h>
struct SlowObject
{
SlowObject() { sleep(1); }
SlowObject(const SlowObject& other) { sleep(1); }
SlowObject& operator=(const SlowObject& other) { sleep(1); return *this; }
};
int main()
{
std::vector<SlowObject> my_vector;
my_vector.resize(3);
for (int i = 0; i < 3; ++ i)
my_vector[i] = SlowObject();
return 0;
}
将浪费你至少9秒的时间来运行
int main()
{
std::vector<SlowObject> my_vector;
my_vector.reserve(3);
for (int i = 0; i < 3; ++ i)
my_vector.push_back(SlowObject());
return 0;
}
只浪费了6秒钟。
std::string
仅在此处复制std::vector
的界面。
答案 1 :(得分:5)
否 - reserve
的目的是阻止重新分配。 resize
设置了可用的大小,reserve
没有 - 它只是设置了保留但尚未直接使用的空间量。
这是一个例子 - 我们将创建一个1000个字符的随机字符串:
static const int size = 1000;
std::string x;
x.reserve(size);
for (int i=0; i<size; i++)
x.push_back((char)rand());
reserve
主要是一个优化工具 - 大多数与reserve
一起使用的代码也可以工作(只是,可能会慢一点)而不调用{{ 1}}。唯一的例外是reserve
可以确保迭代器在没有保留调用的情况下保持有效。
答案 2 :(得分:4)
capacity
是实际缓冲区的长度,但该缓冲区私有到字符串;换句话说,它不是你的访问权限。标准库的std::string
可能分配的内存多于存储字符串实际字符所需的内存。容量是总分配长度。但是,访问s.begin()
和s.end()
之外的字符仍然是非法的。
如果您预计会调整字符串大小以避免不必要的重新分配,请调用reserve
。例如,如果您计划在循环中连接十个20个字符的字符串,那么为字符串保留201个字符(额外的一个用于零终结符)可能是有意义的,而不是从默认大小中多次扩展它
答案 3 :(得分:2)
reserve(n)
确实分配了足够的存储空间来容纳至少n
个元素,但它实际上并没有用任何元素填充容器。该字符串仍为空(大小为0),但您可以保证,在字符串的内部缓冲区需要之前,您可以添加(例如通过push_back
或insert
)至少n
个元素重新分配,而resize(n)
实际上调整字符串的大小以包含n
元素(如果需要,删除或添加新元素)。
所以reserve
实际上只是一个优化工具,当你知道你正在向容器添加一堆元素时(例如在push_back
循环中)并且不希望它重新分配存储经常会导致内存分配和复制成本。但它不会更改字符串的外部/客户端视图。它仍然保持为空(或保持其当前元素数)。
同样capacity
返回字符串可以容纳的元素数,直到需要重新分配其内部存储,而size
(对于字符串也length
)返回实际的元素数在字符串中。
答案 4 :(得分:1)
仅仅因为reserve
分配额外空间并不意味着您访问它是合法的。
在您的示例中,使用resize
,或者将其重写为以下内容:
string my_string;
// I want my string to have 20 bytes long buffer
my_string.reserve( 20 );
int i = 0;
for ( parsing_something_else_loop )
{
char ch = <business_logic>;
// store the character in
my_string += ch;
}
答案 5 :(得分:-1)
std :: vector 而不是 std :: string 也可能是一个解决方案 - 如果没有针对它的要求。
vector<char> v; // empty vector
vector<char> v(10); // vector with space for 10 elements, here char's
你的例子:
vector<char> my_string(20);
int i=0;
for ( parsing_something_else_loop )
{
char ch = <business_logic>;
my_string[i++] = ch;
}