【问题标题】:std::string::reserve() and std::string::clear() conundrumstd::string::reserve() 和 std::string::clear() 难题
【发布时间】:2015-08-11 16:25:39
【问题描述】:

这个问题一开始就有点代码,只是因为我觉得更容易看出我在追求什么:

/*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] = '\0';,但这对我来说很不合适。

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

谢谢,提前。

【问题讨论】:

  • 好问题:clear 永远不会替换缓冲区是一个常见的假设,但我在 C++11 中看不到任何这样的保证。可能存在但不明显。
  • “上面的 reserve() 破坏了我的优化!” 是吗?
  • @LightnessRacesinOrbit 现在不行,不在我当前的平台上。但我想我记得一些平台的实现看起来完全不同。不确定它是否是纯粹的实现之一,或者它是否是 SGI……嗯……那是几年前的事了。
  • @BitTickler:C++ 现在已经标准化了。

标签: c++ string c++11


【解决方案1】:

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

如果没有clear 不会释放的正式保证,而且至少在 C++11 中它似乎不存在,您有以下选择:

  • 忽略问题。
    毕竟,动态缓冲区分配所产生的微秒可能完全无关紧要,此外,即使没有正式的保证,clear 解除分配的机会也非常低。

  • 需要 C++ 实现,其中 clear 不会释放。
    (您可以为此添加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++11 C:\my\so\0284>a 14 14 C:\my\so\0284>cl keep_capacity.cpp /Feb 保持容量.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;
    }
}

【讨论】:

  • 向量在标准中有明确的措辞(“不得重新分配......”,在stackoverflow.com/questions/18467624 中讨论无止境),但 basic_string 没有等价的......似乎是只是未指定 wrt 容量。
  • @Cubbi: 那么上面的Collector 类是不必要的,可以直接使用std::vector。这就是我认为我记得的内容,但我不想坐下来等待标准中可能的野鹅追逐,所以我只是编写了代码。 :)
  • @Cubbi: :( 我查了你所指的答案,嗯,它不存在。特别是“在此之前不得重新分配”的解释作为对 de/re 的保证-allocation,与交换和移动分配不兼容。好的,所以,好的一面:毕竟需要Collector 类,只是为了对事情痛苦地迂腐。
猜你喜欢
  • 2019-09-10
  • 2015-07-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多