std :: strings的capacity(),reserve()& resize()函数

时间:2012-03-01 18:14:41

标签: c++ string stl

我想使用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()之外还做了更多的事情,它只是初始化缓冲区,我们知道它总是很好。我们可以在岛上投票保留()吗?

6 个答案:

答案 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_backinsert)至少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;
}