【问题标题】:C++ string uses maximum buffer allocated?C++ 字符串使用分配的最大缓冲区?
【发布时间】:2011-07-28 12:26:16
【问题描述】:

我声明了一个变量string s;

然后执行s = "abc"; 现在它有 3 个字符的缓冲区。

之后

s = "abcd" 它有一个 4 个字符的缓冲区。

现在在第三个语句之后

s = "ab" 问题是它会保留 4 个字符的缓冲区还是重新分配一个 2 个字符的缓冲区?

如果它将分配 2 个字符缓冲区,我有什么办法可以告诉它保留分配的最大缓冲区。

那么它是否保留了曾经分配的最大大小的缓冲区?

s = "ab"
s="abc"
s="a"
s="abcd"
s="b"

现在它应该保留一个大小为 4 的缓冲区。

这可能吗?

【问题讨论】:

    标签: c++ string stl


    【解决方案1】:

    一旦分配,字符串将保留其缓冲区,并且仅在需要更大的缓冲区时才重新分配。它也可能从大于 3 或 4 的初始缓冲区大小开始。

    您可以使用capacity() 成员函数检查分配的大小。


    在下面詹姆斯的 cmets 之后,我仍然相信我的回答对于问题中给出的示例是正确的。

    但是,对于引用计数的实现,这样的序列

    s = "some rather long string...";
    
    std::string t = "b";
    s = t;
    

    如果实现决定在st 之间共享内部缓冲区,则将s.capacity() 设置为等于t.capacity()

    【讨论】:

    • 嗯,size() 是字符串中的字符数。即使分配了较小的字符串,它也会保留其缓冲区capacity()
    • 这非常依赖于实现,所描述的既不符合 VC++ 也不符合 g++ 的行为。
    • 这正是VC++实现std::string的方式,至少VC2008是这样。初始缓冲区大小为 15,每次需要更大的缓冲区时都会加倍。
    • @Andy 至少在 VC++ 2005 中,缓冲区大小没有翻倍;它以其他倍数(1.5?)增长。但确实它似乎并没有经常减少缓冲区大小。 (g++ 使用完全不同的策略。)
    • @James:有什么不同?你能提供更多细节吗?看到任何减少缓冲区大小而没有像string.swap(std::string()) 这样的特殊技巧的实现,我会感到非常惊讶,因为我经常依赖它来进行 memalloc 优化。 BTH,我确信这是标准要求的。
    【解决方案2】:

    s = "ab" 问题是它会保留 4 个单词的缓冲区还是会保留 重新分配 2 字缓冲区?

    它不会重新分配缓冲区。我不知道标准中是否提到过,但我见过的所有实现只有在需要增加容量时才会发出重新分配。永不减少。即使您有一个包含 4 个字符的字符串并调用 .resize(2).reserve(2),容量也不会改变。为了强制字符串(或容器)重新分配内存以适应确切的大小,有一个简单的swap 技巧

    s.swap(string(s));
    

    这里发生了什么?您从 s 创建一个临时的,其容量与 s.size() 完全相同,然后将其与您的原始字符串交换。临时的析构函数将释放所有必要的资源。

    同样,我并不是说这是标准的,但我见过的所有实现都有这种行为。

    【讨论】:

    • 这取决于实现:如果您为其分配一个具有较小容量的新字符串,g++ 将更改容量,而 VC++ 通常会使用固定容量集中的最小容量。 (我怀疑你在想std::vector;你所描述的既不是 g++ 也不是 VC++ 的行为,我不敢相信你没有看到这些。)
    【解决方案3】:

    您可以通过调用轻松查看实现的行为 std::string::capacity 在不同的时间。一般来说,我会感到惊讶 如果任何实现曾经有一个三个字符的缓冲区。 (不是 字,但字节,至少在大多数现代机器上。)在实践中, 实现会有所不同,并且还会根据新长度的不同而有所不同 出现:用g++,例如用删除字符 std::string::erase 不会减少字符串的容量,但是 分配一个新的、更小的字符串会。 VC++ 不会减少容量 在任一情况下。 (一般来说,VC++和g++有很大的不同 关于字符串内存管理的策略。)

    编辑:

    鉴于其他反应(甚至不符合通常 练习):这是我用来验证我的陈述的小测试程序 上面(虽然我真的不需要 g++——我知道 内部实现非常好):

    #include <string>
    #include <iostream>
    #include <iomanip>
    
    template<typename Traits>
    void
    test()
    {
        std::string s;
        size_t lastSeen = -1;
        std::cout << Traits::name() << ": Ascending:" << std::endl;
        while ( s.size() < 150 ) {
            if ( s.capacity() != lastSeen ) {
                std::cout << "  " << std::setw( 3 ) << s.size()
                    << ": " << std::setw( 3 ) << s.capacity() << std::endl;
                lastSeen = s.capacity();
            }
            Traits::grow( s );
        }
        std::cout << Traits::name() << ": Descending: " << std::endl;
        while ( s.size() != 0 ) {
            Traits::shrink( s );
            if ( s.capacity() != lastSeen ) {
                std::cout << "  " << std::setw( 3 ) << s.size()
                    << ": " << std::setw( 3 ) << s.capacity() << std::endl;
                lastSeen = s.capacity();
            }
        }
        std::cout << "Final: capacity = " << s.capacity() << std::endl;
    }
    
    struct Append
    {
        static void grow( std::string& s )
        {
            s += 'x';
        }
        static void shrink( std::string& s )
        {
            s.erase( s.end() - 1 );
        }
        static std::string name()
        {
            return "Append";
        }
    };
    
    struct Assign
    {
        static void grow( std::string& s )
        {
            s = std::string( s.size() + 1, 'x' );
        }
        static void shrink( std::string& s )
        {
            s = std::string( s.size() - 1, 'x' );
        }
        static std::string name()
        {
            return "Assign";
        }
    };
    
    int
    main()
    {
        test<Append>();
        test<Assign>();
        return 0;
    }
    

    试试看。结果很有启发性。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-03-16
      • 1970-01-01
      • 1970-01-01
      • 2021-07-21
      • 1970-01-01
      • 1970-01-01
      • 2012-04-27
      • 1970-01-01
      相关资源
      最近更新 更多