在不重新分配的情况下重用字符串流
reusing a stringstream without re-allocation
我正在尝试弄清楚如何重用 stringstream 对象,而无需在每次将内容放入流中时重新分配底层字符串。我发现 this answer 导致我这样做:
int main() {
stringstream ss;
int x;
ss << "423";
ss >> x; // x is now 423
ss.clear();
ss.seekg(0);
ss.seekp(0);
ss << "1";
ss >> x; // x is now 123. Instead I want x to be 1.
std::cout << x << std::endl;
}
不幸的是,这不起作用,因为第一次传递的字符串内容仍然存在(第一次传递后字符串为 "423"
,第二次传递后为 "123"
).但是,如果我在第二次放置之后立即添加 space,事情 似乎 可以工作,如下所示:
int main() {
stringstream ss;
int x;
ss << "423";
ss >> x; // x is now 423
ss.clear();
ss.seekg(0);
ss.seekp(0);
ss << "1";
ss << " "; // add a space right after the desired value
ss >> x; // x is now 1
std::cout << x << std::endl;
}
第二次传递后,字符串为 "1 3"
。我对 I/O 库不是很熟悉,我想知道上述方法是否安全,或者它是否恰好适用于这个简单的示例,或者是否有更好的解决方案。实时代码 here。谢谢!
我使用 clang 和这段代码做了一些调查和实验:
测试代码
class LogHelper {
public:
~LogHelper() {
std::cout << out.str() << '\n';
}
std::ostream &stream() {
return out;
}
private:
std::ostringstream out;
};
#define LOG() LogHelper().stream() << __FUNCTION__ << '(' << __LINE__ << ")"
#define VAR(x) ", " #x "[" << x << ']'
class MyAllocator : public std::allocator<char> {
public:
using base = allocator<value_type>;
using base::allocator;
value_type* allocate( std::size_t n, const void * hint) {
LOG() << VAR(n);
return base::allocate(n, hint);
}
value_type* allocate( std::size_t n ) {
LOG() << VAR(n);
return base::allocate(n);
}
void deallocate( value_type* p, std::size_t n ) {
LOG() << VAR(n);
base::deallocate(p, n);
}
};
using MySStream = std::basic_stringstream<char, std::char_traits<char>, MyAllocator>;
using MyString = std::basic_string<char, std::char_traits<char>, MyAllocator>;
int main() {
MySStream ss; // (MyString(255, '[=10=]'));
ss.clear();
int x;
ss << "423";
ss << " 423";
LOG();
ss << " 423jlfskdfjl jfsd sdfdsfkdf dsfg dsfg dfg dfg dsfg df gdf gdfg dsfg dsfgdsfgds";
LOG();
ss >> x;
ss.clear();
ss.str({});
ss.seekg(0);
ss.seekp(0);
ss << "1";
ss >> x;
std::cout << x << std::endl;
LOG();
return 0;
}
main(55)
allocate(34), n[48]
allocate(34), n[96]
deallocate(39), n[48]
main(57)
1
main(70)
deallocate(39), n[96]
- 在流中预设长字符串 clang, visual studio
allocate(34), n[256]
allocate(34), n[256]
deallocate(39), n[256]
main(55)
main(57)
1
main(70)
deallocate(39), n[256]
我有几个发现
- clang 和 visual 的行为相同,gcc 在此代码中存在一些异常问题。
std::basic_stringstream
字符串缓冲区总是增长,从不收缩
std::basic_stringstream
糟透了。您不能保留字符串大小或缓冲区大小,例如 std::string
。自定义分配器只能按类型传递,不能按对象提供分配器。
- 要减少分配,您必须在开始时设置大字符串,然后在您不成功之前不会发生容量重新分配(第二个示例)。
- 提供自定义分配器并没有太大帮助,它会在获取结果字符串时添加样板代码。在我的示例中,它主要用于记录分配和释放。
ss.str({});
不造成分配。这里小字符串优化有帮助
结论:
- 您可以按照您 SO 答案中链接中的建议安全地执行
ss.str({});
,它不会导致分配。这里小字符串优化有帮助,而且
- 自定义分配器不是很有用
- 在乞讨时设置大的虚拟字符串是非常有效的邪恶 hack
- 寻找替代方案应该是更好的方法(也许是提升 - 我没有测试)
- 点
1
,你的问题表明你没有做任何测量,你的问题是基于个人假设。
我正在尝试弄清楚如何重用 stringstream 对象,而无需在每次将内容放入流中时重新分配底层字符串。我发现 this answer 导致我这样做:
int main() {
stringstream ss;
int x;
ss << "423";
ss >> x; // x is now 423
ss.clear();
ss.seekg(0);
ss.seekp(0);
ss << "1";
ss >> x; // x is now 123. Instead I want x to be 1.
std::cout << x << std::endl;
}
不幸的是,这不起作用,因为第一次传递的字符串内容仍然存在(第一次传递后字符串为 "423"
,第二次传递后为 "123"
).但是,如果我在第二次放置之后立即添加 space,事情 似乎 可以工作,如下所示:
int main() {
stringstream ss;
int x;
ss << "423";
ss >> x; // x is now 423
ss.clear();
ss.seekg(0);
ss.seekp(0);
ss << "1";
ss << " "; // add a space right after the desired value
ss >> x; // x is now 1
std::cout << x << std::endl;
}
第二次传递后,字符串为 "1 3"
。我对 I/O 库不是很熟悉,我想知道上述方法是否安全,或者它是否恰好适用于这个简单的示例,或者是否有更好的解决方案。实时代码 here。谢谢!
我使用 clang 和这段代码做了一些调查和实验:
测试代码
class LogHelper {
public:
~LogHelper() {
std::cout << out.str() << '\n';
}
std::ostream &stream() {
return out;
}
private:
std::ostringstream out;
};
#define LOG() LogHelper().stream() << __FUNCTION__ << '(' << __LINE__ << ")"
#define VAR(x) ", " #x "[" << x << ']'
class MyAllocator : public std::allocator<char> {
public:
using base = allocator<value_type>;
using base::allocator;
value_type* allocate( std::size_t n, const void * hint) {
LOG() << VAR(n);
return base::allocate(n, hint);
}
value_type* allocate( std::size_t n ) {
LOG() << VAR(n);
return base::allocate(n);
}
void deallocate( value_type* p, std::size_t n ) {
LOG() << VAR(n);
base::deallocate(p, n);
}
};
using MySStream = std::basic_stringstream<char, std::char_traits<char>, MyAllocator>;
using MyString = std::basic_string<char, std::char_traits<char>, MyAllocator>;
int main() {
MySStream ss; // (MyString(255, '[=10=]'));
ss.clear();
int x;
ss << "423";
ss << " 423";
LOG();
ss << " 423jlfskdfjl jfsd sdfdsfkdf dsfg dsfg dfg dfg dsfg df gdf gdfg dsfg dsfgdsfgds";
LOG();
ss >> x;
ss.clear();
ss.str({});
ss.seekg(0);
ss.seekp(0);
ss << "1";
ss >> x;
std::cout << x << std::endl;
LOG();
return 0;
}
main(55)
allocate(34), n[48]
allocate(34), n[96]
deallocate(39), n[48]
main(57)
1
main(70)
deallocate(39), n[96]
- 在流中预设长字符串 clang, visual studio
allocate(34), n[256]
allocate(34), n[256]
deallocate(39), n[256]
main(55)
main(57)
1
main(70)
deallocate(39), n[256]
我有几个发现
- clang 和 visual 的行为相同,gcc 在此代码中存在一些异常问题。
std::basic_stringstream
字符串缓冲区总是增长,从不收缩std::basic_stringstream
糟透了。您不能保留字符串大小或缓冲区大小,例如std::string
。自定义分配器只能按类型传递,不能按对象提供分配器。- 要减少分配,您必须在开始时设置大字符串,然后在您不成功之前不会发生容量重新分配(第二个示例)。
- 提供自定义分配器并没有太大帮助,它会在获取结果字符串时添加样板代码。在我的示例中,它主要用于记录分配和释放。
ss.str({});
不造成分配。这里小字符串优化有帮助
结论:
- 您可以按照您 SO 答案中链接中的建议安全地执行
ss.str({});
,它不会导致分配。这里小字符串优化有帮助,而且 - 自定义分配器不是很有用
- 在乞讨时设置大的虚拟字符串是非常有效的邪恶 hack
- 寻找替代方案应该是更好的方法(也许是提升 - 我没有测试)
- 点
1
,你的问题表明你没有做任何测量,你的问题是基于个人假设。