std::string::reserve() and std::string::clear() conundrum

std::string::reserve() and std::string::clear() conundrum

本文关键字:std string conundrum clear and reserve      更新时间:2023-10-16

这个问题从一些代码开始,只是因为我认为更容易看到我所追求的东西:

/*static*/ 
void 
Url::Split
(std::list<std::string> & url
, const std::string& stringUrl
)
{
    std::string collector;
    collector.reserve(stringUrl.length());
    for (auto c : stringUrl)
    {
        if (PathSeparator == c)
        {
            url.push_back(collector);
            collector.clear(); // Sabotages my optimization with reserve() above!
        }
        else
        {
            collector.push_back(c);
        }
    }
    url.push_back(collector);
}

在上面的代码中,collector.reserve(stringUrl.length());行应该减少在下面的循环中执行的堆操作量。毕竟,每个子字符串不能超过整个 url,因此像我一样保留足够的容量看起来是个好主意。

但是,一旦子字符串完成并将其添加到 url 零件列表中,我需要以一种或另一种方式将字符串重置为长度 0。简短的"窥视定义"检查向我表明,至少在我的平台上,保留的缓冲区将被释放,因此,我的 reserve(( 调用的目的受到损害。

在内部,如果清楚,它会调用一些_Eos(0)

我也可以用collector.resize(0)完成相同的任务,但偷看定义显示它也在内部调用_Eos(newsize),因此行为与调用clear()的情况相同。

现在的问题是,是否有一种便携式方法来建立预期的优化,以及哪个std::string功能可以帮助我。

当然,我可以写collector[0] = '';但这对我来说看起来很不对劲。

旁注:虽然我发现了类似的问题,但我不认为这是其中任何一个的重复。

谢谢,提前。

在 C++11 标准中,clear 是根据 erase 定义的,这被定义为值替换。没有明显的保证缓冲区未解除分配。它可能在那里,隐含在其他东西中,但我没有找到任何这样的。

如果没有正式保证clear不会解除分配,并且似乎至少从 C++11 开始它不存在,您有以下选择:

  • 忽略问题。
    毕竟,动态缓冲区分配产生的微秒数很可能是绝对无关紧要的,此外,即使没有正式的保证,clear释放的可能性也非常低。

  • 需要clear不解除分配的C++实现。
    (您可以为此效果添加assert,选中.capacity()

  • 执行自己的缓冲区实现。


忽略问题似乎是安全的,即使分配(如果执行(在时间紧迫的情况下也是如此,因为对于常见的实现clear不会减少容量

例如,这里以 g++ 和 Visual C++ 为例:

#include <iostream>
#include <string>
using namespace std;
auto main() -> int
{
    string s = "Blah blah blah";
    cout << s.capacity();
    s.clear();
    cout << ' ' << s.capacity() << endl;
}
C:\my\so\0284>g++ keep_capacity.cpp -std=c++11C:\my\so\0284>a14 14C:\my\so\0284>cl keep_capacity.cpp/二月keep_capacity.cppC:\my\so\0284>b15 15C:\my\so\0284>_

如果您真的想走那么远,可以按以下步骤进行自己的缓冲区管理:

#include <iostream>
#include <string>
#include <vector>
namespace my {
    using std::string;
    using std::vector;
    class Collector
    {
    private:
        vector<char>    buffer_;
        int             size_;
    public:
        auto str() const
            -> string
        { return string( buffer_.begin(), buffer_.begin() + size_ ); }
        auto size() const -> int { return size_; }
        void append( const char c )
        {
            if( size_ < int( buffer_.size() ) )
            {
                buffer_[size_++] = c;
            }
            else
            {
                buffer_.push_back( c );
                buffer_.resize( buffer_.capacity() );
                ++size_;
            }
        }
        void clear() { size_ = 0; }
        explicit Collector( const int initial_capacity = 0 )
            : buffer_( initial_capacity )
            , size_( 0 )
        { buffer_.resize( buffer_.capacity() ); }
    };
    auto split( const string& url, const char pathSeparator = '/' )
        -> vector<string>
    {
        vector<string>  result;
        Collector       collector( url.length() );
        for( const auto c : url )
        {
            if( pathSeparator == c )
            {
                result.push_back( collector.str() );
                collector.clear();
            }
            else
            {
                collector.append( c );
            }
        }
        if( collector.size() > 0 ) { result.push_back( collector.str() ); }
        return result;
    }
}  // namespace my
auto main() -> int
{
    using namespace std;
    auto const url = "http://en.wikipedia.org/wiki/Uniform_resource_locator";
    for( string const& part : my::split( url ) )
    {
        cout << '[' << part << ']' << endl;
    }
}