std::string::reserve() 和 std::string::clear() 难题

Bit*_*ler 5 c++ string c++11

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

/*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);
}
Run Code Online (Sandbox Code Playgroud)

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

但是,一旦子字符串完成并将其添加到 url 部分列表中,我需要以某种方式将字符串重置为长度 0。简短的“查看定义”检查表明,至少在我的平台上,保留的缓冲区将被释放,这样,我的 Reserve() 调用的目的就会受到损害。

_Eos(0)在内部它会在清除的情况下调用一些。

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

现在的问题是,是否有一种可移植的方法来建立预期的优化,以及哪个std::string函数可以帮助我实现这一点。

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

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

提前致谢。

Che*_*Alf 4

在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;
}
Run Code Online (Sandbox Code Playgroud)
C:\my\so\0284> g++ keep_capacity.cpp -std=c++11

C:\my\so\0284> a
14 14

C:\my\so\0284> cl keep_capacity.cpp /Feb
keep_capacity.cpp

C:\my\so\0284> b
15 15

C:\我的\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;
    }
}
Run Code Online (Sandbox Code Playgroud)