这个问题以一些代码开始,只是因为我认为更容易看到我所追求的内容:
/*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';但这对我来说看起来很不合适。
旁注:虽然我发现了类似的问题,但我认为这不是其中任何一个的重复。
提前致谢。
在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)