C++学习(二):学会使用stringstream

1.前言 

  今天在CppTemplateTutorial群里,有人问了一个问题:这一堆add怎么简化掉 https://wandbox.org/permlink/vDPDwMFbBIQSSymS。代码如下:

 1 #include <variant>
 2 #include <map>
 3 #include <string>
 4 #include <cassert>
 5 #include <iostream>
 6 struct myval : public std::variant<int, double, std::string>
 7 {
 8     using base = std::variant<int, double, std::string>;
 9     myval() : base() {}
10     myval(int v) : base(v) {}
11     myval(double v) : base(v) {}
12     myval(std::string v) : base(v) {}
13     
14     myval(myval const& v) : base(v) {}
15     myval(myval&& v) : base(std::move(v)) {}
16 };
17 
18 template<class T, class U>
19 myval add(T const& a, U const& b) {
20     return a + b;
21 }
22 
23 myval add(std::string const& a, int b) {
24     return a + std::to_string(b);
25 }
26 myval add(std::string const& a, double b) {
27     return a + std::to_string(b);
28 }
29 myval add(std::string const& a, std::string const& b) {
30     return a + b;
31 }
32 
33 myval add(int a, std::string const& b) {
34     return std::to_string(a) + b;
35 }
36 myval add(double a, std::string const& b) {
37     return std::to_string(a) + b;
38 }
39 
40 myval operator+(myval const& a, myval const& b)
41 {
42     return std::visit([](auto& a, auto& b) { return add(a, b); }, a, b);
43 }
44 
45 std::ostream& operator<<(std::ostream& os, myval const& b)
46 {
47     return std::visit([&](auto b)->std::ostream& { os << b; return os; }, b);
48 }
49 
50 int main() {
51     myval a = 1;
52     myval b = 10.0;
53     myval c = a + b;
54     std::map<std::string, myval> m{{"1", a}, {"2", b}, {"3", c}};
55     if (m.find("3") == m.end())
56     {
57         std::cout << m["3"];
58     }
59 }
View Code

  一种解决方法是:给to_string加个string重载,不过这并不是最好的办法。另一种方法是使用stringstream。不过,想到前一段时间,我还经常用C语言的sprintf,我突然觉得有点好笑。

  处理问题的优先方式应该是充分考虑语言的特性,使得问题处理起来更加简洁高效一点。学会使用stringstream,可以避免一些由C语言中类似atoi,itoa ,strtol以及sprintf带来的问题。

  那么,这个问题如果使用stringstream可以这样写:

  

2.使用stringstream对象简化类型转换

  C++标准库中的<sstream>提供了比ANSI C的<stdio.h>更高级的一些功能,即单纯性、类型安全和可扩展性。

  为什么要学习stringstream。如果你已习惯了<stdio.h>风格的转换,也许你首先会问:为什么要花额外的精力来学习基于<sstream>的类型转换呢?也许对下面一个简单的例子的回顾能够说服你。假设你想用sprintf()函数将一个变量从int类型转换到字符串类型。为了正确地完成这个任务,你必须确保证目标缓冲区有足够大空间以容纳转换完的字符串。此外,还必须使用正确的格式化符。如果使用了不正确的格式化符,会导致非预知的后果。下面是一个例子:

int n=10000; chars[10]; sprintf(s,"%d",n);// s中的内容为"10000" 

  到目前为止看起来还不错。但是,对上面代码的一个微小的改变就会使程序崩溃: 

int n=10000;char s[10];sprintf(s,"%f",n);// 看!错误的格式化符 

  在这种情况下,程序员错误地使用了%f格式化符来替代了%d。因此,s在调用完sprintf()后包含了一个不确定的字符串。要是能自动推导出正确的类型,那不是更好吗?

  <sstream>库定义了三种类:istringstream、ostringstream和stringstream,分别用来进行流的输入、输出和输入输出操作。另外,每个类都有一个对应的宽字符集版本。简单起见,我主要以stringstream为中心,因为每个转换都要涉及到输入和输出操作。
  注意,<sstream>使用string对象来代替字符数组。这样可以避免缓冲区溢出的危险。而且,传入参数和目标对象的类型被自动推导出来,即使使用了不正确的格式化符也没有危险。

  在过去留下来的程序代码和纯粹的C程序中,传统的<stdio.h>形式的转换伴随了我们很长的一段时间。但是,如文中所述,基于stringstream的转换拥有类型安全和不会溢出这样抢眼的特性,使我们有充足得理由抛弃<stdio.h>而使用<sstream>。<sstream>库还提供了另外一个特性—可扩展性。你可以通过重载来支持自定义类型间的转换。

3.参考来源:

  http://www.cppblog.com/Sandywin/archive/2007/07/13/27984.html

  http://developer.zhiding.cn/2003/0304/83252.shtml

    http://www.cnblogs.com/gamesky/archive/2013/01/09/2852356.html

  http://www.cnblogs.com/james6176/p/3222671.html

  http://www.cnblogs.com/lzjsky/archive/2011/01/04/1925538.html 

posted @ 2017-05-02 23:02  星云的彼岸  阅读(676)  评论(0编辑  收藏  举报