c ++ std :: ostringstream vs std :: string :: append

Nic*_*oft 32 c++ string stream

在使用某种缓冲的所有示例中,我看到它们使用流而不是字符串.std :: ostringstream和<<运算符与使用string.append有何不同.哪一个更快,哪一个使用更少的资源(内存).

我知道的一个区别是你可以输出不同的类型到输出流(如整数),而不是字符串:: append接受的有限类型.

这是一个例子:

std::ostringstream os;
os << "Content-Type: " << contentType << ";charset=" << charset << "\r\n";
std::string header = os.str();
Run Code Online (Sandbox Code Playgroud)

VS

std::string header("Content-Type: ");
header.append(contentType);
header.append(";charset=");
header.append(charset);
header.append("\r\n");
Run Code Online (Sandbox Code Playgroud)

显然使用流更短,但我认为append返回对字符串的引用,因此它可以像这样写:

std::string header("Content-Type: ");
header.append(contentType)
  .append(";charset=")
  .append(charset)
  .append("\r\n");
Run Code Online (Sandbox Code Playgroud)

使用输出流,您可以执行以下操作:

std::string content;
...
os << "Content-Length: " << content.length() << "\r\n";
Run Code Online (Sandbox Code Playgroud)

但是内存使用和速度呢?特别是在大循环中使用时.

更新:

更清楚的问题是:我应该使用哪一个?为什么?是否有首选或其他的情况?对于性能和内存......我认为基准测试是唯一的方法,因为每个实现都可能有所不同.

更新2:

好吧,我不清楚我应该从答案中使用什么,这意味着他们中的任何一个都会完成这项任务,再加上矢量. Cubbi在DietmarKühl的基础上做了很好的基准测试,最大的不同是这些物体的构造.如果您正在寻找答案,您也应该检查一下.我会等待更多的其他答案(看看以前的更新),如果我没有得到一个,我想我会接受Tolga的答案,因为他的建议使用矢量已经完成之前意味着矢量应该是更少的资源饥饿.

Cub*_*bbi 22

构造一个流对象是一个比构造一个字符串对象更复杂的操作,因为它必须保持(并因此构造)其std::locale成员,以及维持状态所需的其他东西(但是区域设置是最大的边界) .

附加类似:都保持连续的字符数组,当超出容量时分配更多.我能想到的唯一区别是,当附加到流时,每次溢出都有一个虚拟成员函数调用(除了内存分配/复制,无论如何都支配溢出处理),并且operator<<必须对流进行一些额外的检查.州.

另外,请注意您正在调用str(),它会再次复制整个字符串,因此根据您的代码编写的内容,流示例会做得更多,而且应该更慢.

我们来测试一下:

#include <sstream>
#include <string>
#include <numeric>

volatile unsigned int sink;
std::string contentType(50, ' ');
std::string charset(50, ' ');
int main()
{
 for(long n = 0; n < 10000000; ++n)
 {
#ifdef TEST_STREAM    
    std::ostringstream os;
    os << "Content-Type: " << contentType << ";charset=" << charset << "\r\n";
    std::string header = os.str();
#endif
#ifdef TEST_STRING
    std::string header("Content-Type: ");
    header.append(contentType);
    header.append(";charset=");
    header.append(charset);
    header.append("\r\n");
#endif
    sink += std::accumulate(header.begin(), header.end(), 0);
 }
}
Run Code Online (Sandbox Code Playgroud)

这是1000万次重复

在我的Linux上,我得到了

                   stream         string
g++ 4.8          7.9 seconds      4.4 seconds
clang++/libc++  11.3 seconds      3.3 seconds
Run Code Online (Sandbox Code Playgroud)

所以,对于这个用例,在这两个实现中,字符串看起来工作得更快,但显然两种方式都有很多改进(保留()字符串,将流构造移出循环,使用不需要的流复制以访问其缓冲区等)

  • 稍微改变设置以在循环外部构造流并仅仅重置它(`os.str("")`)以有趣的方式改变数字:流在gcc上现在更快但在clang上更慢.我得到gcc/string = 4.5s,gcc/stream = 2.5s,clang/string = 2.25s,clang/stream = 4.1s:很好地越过了;) (10认同)
  • 所以除非你每次都在构造流实际上与使用字符串相当。 (2认同)

Eth*_*one 10

std::ostringstream不一定存储为内存中的连续字符数组.在发送这些HTTP头时,您实际上需要连续的字符数组,并且可能会复制/修改内部缓冲区以使其成为顺序.

std::string使用适当的std::string::reserve没有理由比std::ostringstream这种情况更慢.

但是,std::ostringstream如果你完全不知道你必须保留的大小,可能会更快地追加.如果你使用std::string和你的字符串增长,它最终需要重新分配和复制整个缓冲区.std::ostringstream::str()与多次重新分配相比,最好使用一次来使数据顺序完成.

PS Pre-C++ 11 std::string也不需要是顺序的,而几乎所有的库都是按顺序实现的.您可能会冒险或使用它std::vector<char>.您需要使用以下内容进行追加:

char str[] = ";charset=";
vector.insert(vector.end(), str, str + sizeof(str) - 1);
Run Code Online (Sandbox Code Playgroud)

std::vector<char>对于性能来说是最好的,因为它最有可能构建起来更便宜,但std::string与构建它们的实际时间相比,它可能并不重要.我做过类似你正在尝试的事情并且std::vector<char>之前一直在使用.纯粹是出于逻辑原因; 矢量似乎更适合工作.你实际上并不想要字符串操作等.此外,我后来做的基准测试证明它表现更好,或者可能只是因为我没有很好地实现操作std::string.

在选择时,满足您需求和最小额外功能的容器通常可以最好地完成工作.

  • @NickSoft http://en.cppreference.com/w/cpp/container/vector:元素是连续存储的,这意味着元素不仅可以通过迭代器访问,还可以使用常规指向元素的偏移量来访问.这意味着指向向量元素的指针可以传递给任何需要指向数组元素的指针的函数.(顺便说一句,我建议使用cppreference.com查找内容) (3认同)
  • 在飞行中不断增长的缓冲是违反廉价的. (2认同)