在所有使用某种缓冲的示例中,我看到它们使用流而不是字符串。std::ostringstream 和 <<运算符与使用 string.append 有何不同。哪个更快,哪个使用更少的资源(内存)。
我知道的一个区别是您可以将不同类型的输出到输出流(如整数)中,而不是 string::append 接受的有限类型。
下面是一个示例:
std::ostringstream os;
os << "Content-Type: " << contentType << ";charset=" << charset << "rn";
std::string header = os.str();
与
std::string header("Content-Type: ");
header.append(contentType);
header.append(";charset=");
header.append(charset);
header.append("rn");
显然使用流更短,但我认为附加对字符串的返回引用,因此可以这样写:
std::string header("Content-Type: ");
header.append(contentType)
.append(";charset=")
.append(charset)
.append("rn");
使用输出流,您可以执行以下操作:
std::string content;
...
os << "Content-Length: " << content.length() << "rn";
但是内存使用量和速度呢?尤其是在大循环中使用时。
更新:
为了更清楚,问题是:我应该使用哪一个,为什么?是否存在一种或另一种首选的情况?为了性能和内存...好吧,我认为基准测试是唯一的方法,因为每个实现都可能不同。
更新 2:
好吧,我不清楚我应该从答案中使用什么,这意味着它们中的任何一个都可以完成这项工作,加上矢量。 Cubbi
做了很好的基准测试,增加了Dietmar Kühl,最大的区别是这些对象的构造。如果您正在寻找答案,也应该检查一下。我会再等一会儿其他答案(查看之前的更新),如果我没有得到答案,我想我会接受 Tolga 的答案,因为他之前已经完成了使用矢量的建议,这意味着矢量应该更少资源需求。
流对象比构造字符串对象要复杂得多,因为它必须保存(因此构造)其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 << "rn";
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("rn");
#endif
sink += std::accumulate(header.begin(), header.end(), 0);
}
}
这是1000万次重复
在我的 Linux 上,我得到
stream string
g++ 4.8 7.9 seconds 4.4 seconds
clang++/libc++ 11.3 seconds 3.3 seconds
因此,对于此用例,在这两个实现中,字符串似乎工作得更快,但显然这两种方式都有很多需要改进的地方(reserve()字符串,将流构造移出循环,使用不需要复制的流来访问其缓冲区等)
std::ostringstream
不一定作为字符的顺序数组存储在内存中。在发送这些 HTTP 标头时,您实际上需要具有连续的字符数组,并且可能会复制/修改内部缓冲区以使其顺序化。
在这种情况下,使用适当的std::string::reserve
std::string
没有理由比std::ostringstream
行动慢。
但是,如果您完全不知道必须保留的大小,std::ostringstream
附加可能会更快。如果使用 std::string
并且字符串增长,则最终需要重新分配和复制整个缓冲区。与否则会发生的多次重新分配相比,最好使用一个std::ostringstream::str()
一次使数据按顺序排列。
附言:C++11 之前的std::string
也不要求是顺序的,而几乎所有的库都将其实现为顺序的。您可以冒险或使用std::vector<char>
。您需要使用以下方法来执行追加:
char str[] = ";charset=";
vector.insert(vector.end(), str, str + sizeof(str) - 1);
std::vector<char>
对性能来说是最好的,因为它很可能更便宜,但与std::string
和它们构建的实际时间相比,它可能并不重要。我做过一些类似于你正在尝试的事情,并与std::vector<char>
一起去过。纯粹是因为逻辑原因;矢量似乎更适合这份工作。您实际上并不想要字符串操作之类的。此外,我后来做的基准测试证明它的性能更好,或者可能只是因为我没有很好地实现std::string
操作。
在选择时,具有需求要求和最少额外功能的容器通常可以最好地完成工作。
使用 stream,您可以让您的类Myclass
覆盖<<
操作,以便您可以编写
MyClass x;
ostringstream y;
y << x;
对于追加,您需要有一个 ToString 方法(或类似的东西),因为您无法覆盖字符串的追加函数。
对于某些代码段,请使用您觉得更舒服的任何代码。将流用于较大的项目,在这些项目中,能够简单地流式传输对象很有用。
如果您担心速度,则应进行分析和/或测试。从理论上讲,std::string::append
不应该变慢,因为它更简单(流必须处理语言环境,不同的格式并且更通用)。但是,只有通过测试才能实现一种或另一种解决方案的真正速度。