这篇文章主要着眼于STL的容器、迭代器与成员函数,不包含头文件<algorithm>中众多算法函数模板的记录。文章还包含运算符重载、泛型程序设计、函数对象概念的介绍。

其他C++必要部分希望留到以后能补充起来。

运算符重载

运算符重载就是对已有的运算符赋予多种含义,使同一运算符作用于不同类型的数据时产生不同的行为,运算符重载的目的是使得C++中的运算符也能够用来操作对象。运算符重载的本质就是编写以运算符作为名称的函数,称之为运算符函数。

包含被重载的运算符的表达式会被编译成对运算符函数的调用,运算符的操作数称为函数调用时的实参,运算的结果就是函数的返回值。举例如下:

#include <iostream>
using namespace std;
class Complex {
  public:
    double real, imag;
    Complex(double r = 0.0, double i = 0.0): real(r), imag(i) { }
    Complex operator - (const Complex & c);
};
Complex operator + (const Complex & a, const Complex & b) {
  return Complex(a.real + b.real, a.imag + b.imag);  //返回一个临时对象
}
Complex Complex::operator - (const Complex & c) {
  return Complex(real - c.real, imag - c.imag);  //返回一个临时对象
}
int main() {
  Complex a(4, 4), b(1, 1), c;
  c = a + b;
  cout << c.real << "," << c.imag << endl;
  cout << (a - b).real << "," << (a - b).imag << endl;
  return 0;
}

上面例子中+重载为一个全局函数,-重载为一个成员函数。运算符重载为全局函数时,参数个数等于运算符的操作数个数,重载为成员函数时,参数个数为操作数个数减1。

有时候将运算符重载为成员函数不能满足要求,重载为全局函数又不能访问类的私有成员,因此就需要将运算符重载为友元。比如说将+重载为Complex类的成员函数时能解释c + 5,但是不能解释5 + c,因为编译器是将前面一个元素当作类的对象的,后面那种表达方法会误认为c也是基本数据类型。所以应该这样:

class Complex {
  double real, imag;
  public:
    Complex(double r, double i): real(r), imag(i) {};
    friend Complex operator + (double r, const Complex & c);
}
Complex operator + (double r, const Complex & c) {
  return Complex(c.real + r, c.imag);
}

STL中使用算法时一定要将准备改动的运算符重载为全局函数,所以最好使用友元的操作。

函数模板与类模板

泛型程序设计是一种算法在实现时不指定具体要操作的数据类型的程序设计方法。在设计程序时可以构造函数模板和类模板,在需要用到时进行函数或类的实例化,得到模板函数和类函数。

编译器由模板自动生成函数时,会用具体的类型名对模板中所有的类型参数进行替换,其他部分则原封不动地保留。编译器会自动根据实参的类型判断如何替换模板中的类型参数(因为可以重载),也可以具体指定类型。下面是一段调用模板函数的示例代码,类模板的使用与此类似。

#include <iostream>
using namespace std;
template<class T>
T Inc(int n) {
  return 1 + n;
}
int main() {
  cout << Inc<double>(4)/2;
  return 0;
}

STL中的基本概念

容器与迭代器

容器是用于存放数据的类模板,有以下几类,以后将容器和容器适配器统称为容器。

  • 顺序容器:元素在容器中的位置与容器的值无关,容器不是排序的。有可变长动态数组vector,双端队列deque,双向链表list
  • 关联容器:容器内的元素是排序的,默认从小到大排,查找性能非常好。有setmultisetmapmultimap
  • 在上面两种的基础上实现容器适配器:栈stack,队列queue,优先级队列priority_queue

要访问顺序容器和关联容器中的元素,需要通过迭代器进行,其和指针很像。四种迭代器及其定义方法如下:

类型 定义方法
正向迭代器 容器类名::iterator 迭代器名;
常量正向迭代器 容器类名::const_iterator 迭代器名;
反向迭代器 容器类名::reverse_iterator 迭代器名;
常量反向迭代器 容器类名::const_reverse_iterator 迭代器名;
  • *迭代器名表示迭代器所指向的元素。
  • 通过非常量迭代器可以修改其指向的元素。
  • 迭代器都可以进行++操作,对于正向迭代器进行++会指向后一个元素,对反向迭代器进行++会指向前一个元素。
  • 容器适配器栈stack,队列queue,优先级队列priority_queue没有迭代器。

通过迭代器遍历vector容器所有元素的示例代码如下。

#include <iostream>
#include <vector>
using namespace std;
int main() {
  vector<int> v;
  for(int n = 0; n < 5; ++n) {
    v.push_back(n);  //该函数在容器尾部添加一个元素
  }
  vector<int>::iterator i;  //vector是类模板,实例化后得到类,vector<int>即类名
  for(i = v.begin(); i != v.end(); ++i) {
    cout << *i << " ";
    *i *= 2;
  }
  cout << endl;
  for(vector<int>::reverse_iterator j = v.rbegin(); j != v.rend(); ++j) {
    cout << *j << " ";
  }
  return 0;
}

该段代码的输出结果是:

0 1 2 3 4
8 6 4 2 0

注意:上述代码for循环体中使用了++i而不是i++,这是因为前者更高效,注意养成好习惯。

容器常见成员函数

有了迭代器的概念后介绍容器的一些成员函数。

所有容器(包括容器适配器)都有以下两个成员函数:

  • int size():返回容器中元素的个数。
  • bool empty():判断容器对象是否为空。

顺序容器和关联容器还有以下成员函数:

  • begin():返回指向容器第一个元素的迭代器
  • end():返回指向容器最后一个元素后面位置迭代器
  • rbegin():返回指向容器最后一个元素的反向迭代器
  • rend():返回指向容器第一个元素前面位置的反向迭代器
  • erase(...):从容器中删除一个或几个元素,有复杂参数。
  • clear():从容器中删除所有元素。

如果一个容器是空的,则begin()end()的返回值相等,rbegin()rend()的返回值相等。

顺序容器还有以下常用成员函数:

  • front():返回容器中第一个元素的引用。
  • back():返回容器中最后一个元素的引用。
  • push_back():在容器末尾添加新元素。
  • pop_back():删除容器末尾的元素。
  • insert(...):插入一个或多个元素,有复杂参数。

迭代器功能分类

常见的迭代器按功能由弱到强分为三种:

  • 正向迭代器:假设p是一个正向迭代器,其支持++p, p++, *p,还可用==!=进行比较。

  • 双向迭代器: 有正向迭代器的所有功能,还支持--pp--,功能定义与++相反。

  • 随机访问迭代器:有双向迭代器的所有功能,还支持下列:

    • p += i:使得p往后移动i个元素。
    • p -= i:使得p往前移动i个元素。
    • p + i:返回p后面第i个元素的迭代器。
    • p - i:返回p前面第i个元素的迭代器。
    • p[i]:返回p后面第i个元素的引用。

    此外两个随机访问迭代器还可以用<><=>=进行比较。p1 < p2表示p1经过若干次++后等于p2

不同容器的迭代器功能如下:

容器 迭代器功能
vector 随机访问
deque 随机访问
list 双向
set/multiset 双向
map/multimap 双向
stack 不支持迭代器
queue 不支持迭代器
priority_queue 不支持迭代器

vector支持随机访问迭代器,所以其遍历也有若干种方法:

for(int i = 0; i < v.size(); ++i) cout << v[i];  //可以像普通数组一样使用vector容器
vector<int>::iterator i;
for(i = v.begin(); i != v.end(); ++i) cout << *i;
for(i = v.begin(); i < v.end(); ++i) cout << *i;

如果是容器list就不可以用<,因为其是双向迭代器。

在C++中数组也是容器,指针就是其迭代器,所以一些STL的算法(函数模板)也可以应用于数组。

迭代器还有辅助函数(要包含头文件<algorithm>):

  • advance(p, n):使迭代器p向后移动n个元素(如果是负数则向前移动)。
  • distance(p, q):计算两个迭代器之间的距离。
  • iter_swap(p, q):用于交换两个迭代器p, q指向的值

STL中的大小概念

STL中默认情况是通过<运算符比较大小的,与>运算符无关。

STL中“x和y相等”往往不等价于“x==y为真”:

  • 在未排序的区间进行的算法,比如顺序查找算法find(),查找过程中用的是==
  • 在排好序的区间上进行查找和合并等算法,比如折半查找binary_search()和关联容器自身的成员函数find(),“x和y相等”是和“x<y和y<x同时为假”等价的,与==运算符无关。

所以记得必要时将<进行必要的重载,且最好重载为全局函数

顺序容器

动态数组vector

需要头文件<vector>,可变长动态数组是通过动态分配的数组来存放元素的,所以随机访问元素快,在尾部添加元素大多数时候快(有时满了需要重新开辟就慢),中间插入/删除元素满。

几个构造函数如下:

  • vector():无参构造函数,将容器初始化为空。

  • vector(int n) :将容器初始化为有n个元素,如果是int型则初始值为0。

  • vector(int n, const T & val):将容器初始化为有n个元素,每个值都是val。比如:

    vector v(4, 100);  //4个元素都是100
  • vector(iterator first, iterator last):将vector容器的内容变成与其他容器上区间[first, last)的内容一致。

    int a[5] = {1, 2, 3, 4, 5};  //数组也看作是容器
    vector v(a, a+5);

具体介绍一下插入和删除成员函数:

  • iterator insert(iterator i, const T & cal):将val插入迭代器i所指向的位置并返回该迭代器。
  • iterator insert(iterator i, iterator first, iterator last):将其他容器上区间[first, last)的内容插入迭代器i所指向的位置。
  • iterator erase(iterator i):删除迭代器i所指向的元素,返回值是被删元素后面的元素的迭代器。
  • iterator erase(iterator first, iterator last):删除容器中的区间[first, last),返回所删除区间的后面一个元素的迭代器,其实就是迭代器last
int a[5] = {1, 2, 3, 4, 5};  //数组也看作是容器
vector<int> v(a, a+5);
v.insert(v.begin() + 2, 13);  //得到1, 2, 13, 3, 4, 5
v.erase(v.begin() + 2);  //得到1, 2, 3, 4, 5
vector<int> v2(4, 100);
v2.insert(v2.begin(), v.begin() + 1, v.begin() + 3);  //得到2, 3, 100, 100, 100, 100
v.erase(v.begin() + 1, v.begin() + 3);  //得到1, 4,5

注意:表述区间时总是左闭右开。

vector还能以嵌套的形式得到可变长的二维数组。

#include <iostream>
#include <vector>
using namespace std;
int main() {
  vector< vector<int> > v(3);
  for(int i = 0; i < v.size(); ++i) {
    for(int j = 0; j < 4; ++j) {
      v[i].push_back(j);
    }
  }
  for(int i = 0; i < v.size(); ++i) {
    for(int j = 0; j < v[i].size(); ++j) {
      cout << v[i][j] << " ";
    }
    cout << endl;
  }
  return 0;
}

注意:在Dev C++中定义二维数组时,尖括号内的尖括号要有空格隔开,否则>>会被编译器识别为右移运算符。

双向链表list

list容器不支持按下标随机存取元素,但其插入和删除中间的元素效率比较高。list容器特有的一些成员函数:

  • void push_front(const T & val):将val插入链表的最前面。
  • void pop_front():删除链表最前面的元素。
  • void sort():将链表从小到大排序。
  • void remove(const T & val) :删除和val相等的元素。
  • remove_if:删除符合某种条件的元素。
  • void unique():删除所有和前一个元素相等的元素。
  • void merge(list<T> & x):将链表x合并进来并清空x,要求链表自身和x都是有序的,最后得到一个有序的链表,就像归并排序中的合并操作一样。
  • void splice(iterator i, list<T> & x, iterator first, iterator last):在位置i前插入链表x中的区间[first, last),并在链表x中删除该区间。链表自身和x可以是同一个链表,只要i不在[first, last)中即可。

注意:STL中的算法sort可以用来对vector和deque进行排序,但需要随机访问迭代器的支持。而list不支持随机访问迭代器所以不能用sort函数对list容器排序,所以容器引入了sort成员函数来完成排序。

可以用list解决约瑟夫问题:n个人围成一圈,编号1~n,从1开始报数,报到m的退出,接着往下报,输出最后留下的人的编号。

#include <iostream>
#include <list>
using namespace std;
int main() {
  list<int> peoples;
  int n, m;
  while(true) {
    cin >> n >> m;
    if(n == 0 && m == 0) break;
    peoples.clear();
    for(int i = 1; i <= n; ++i) {
      peoples.push_back(i);
    }
    list<int>::iterator it = peoples.begin();
    while(peoples.size() > 1) {  //只要多于1个人就要找出一个退出
      for(int i = 1; i < m; ++i) {
        ++it;
        if(it == peoples.end())
          it = peoples.begin();  //循环回去
      }
      it = peoples.erase(it);  //报m的人退出,后一个人的迭代器赋值给it
      if(it == peoples.end())
        it = peoples.begin();  //人退出相当于报最后一个数,也需要循环回去
    }
    cout << peoples.front() << endl;
  }
  return 0;
}

双向队列deque

也是可变长数组。vector在头部删除添加元素的速度很慢,在尾部删除添加的性能较好。而deque的优点是在头部和尾部增删元素都具有较好的性能。而vector随机存取任何元素的速度比deque要快。deque有两种vector没有的成员函数。

  • void push_front(const T & val)
  • void pop_front()

函数对象

如果一个类将()运算符重载为成员函数,这个类就称为函数对象类,这个类的对象就叫做函数对象。举例如下。

#include <iostream>
using namespace std;
class CAverage {
  public:
    double operator() (int a1, int a2, int a3) {
      return (double)(a1 + a2 + a3) / 3;  //重载运算符
    }
}
int main() {
  CAverage average;
  cout << average(3, 2, 3);
  return 0;
}

函数对象在STL中有着实际的作用。比如sort算法有两个版本:

  • 第一个版本通过运算符<进行,所以待排序的对象必须能用<运算符进行比较。

    template 
    void sort(_RandIt first, _RandIt last);
  • 第二个版本就可以自定义规则。

    template 
    void sort(_RandIt first, _RandIt last, Pred op);

自定义比较规则op的举例如下:

#include <iostream>
#include <algorithm>
using namespace std;
template <class T>
void PrintInterval(T first, T last) {
  for(; first != last; ++first) {
    cout << *first << " ";
  }
  cout << endl;
}

class A {
  public:
    int v;
    A(int n): v(n) {}
};

bool operator < (const A & a1, const A & a2) {
  return a1.v < a2.v;
}
bool GreaterA(const A & a1, const A & a2) {
  return a1.v > a2.v;  //v值大的元素作为较小的数
}
struct LessA {
  bool operator () (const A & a1, const A & a2) {
    return (a1.v % 10) < (a2.v % 10);  //个位数较小的元素作为较小的数
  }
};
ostream & operator << (ostream & o, const A & a) {
  o << a.v;
  return o;
}
int main() {
  int a1[4] = {5, 2, 4, 1};
  A a2[5] = {13, 12, 9, 8, 16};
  sort(a1, a1 + 4);
  PrintInterval(a1, a1 + 4);  //1 2 4 5
  sort(a2, a2 + 5);
  PrintInterval(a2, a2 + 5);  //8 9 12 13 16
  sort(a2, a2 + 5, GreaterA);
  PrintInterval(a2, a2 + 5);  //16 13 12 9 8
  sort(a2, a2 + 5, LessA());
  PrintInterval(a2, a2 + 5);  //12 13 16 8 9
  return 0;
}

注意:LessA作为函数参数时记得加上()运算符,这才会被视作一个函数。

STL中自带一些函数对象类模板,请参考C++手册。

关联容器

概述与预备

  • set:排好序的集合,不允许有相同元素。
  • multiset:排好序的集合,允许有相同元素。
  • map:每个元素都分为关键字和值两部分,容器中的元素是按关键字排序的,不允许有多个元素的关键字相同。
  • multimap:允许有多个元素的关键字相同。

前两个的头文件是<set>,后两个是<map>

不能修改setmultiset容器中元素的值,因为修改元素后容器并不会自动调整顺序,正确的做法是先删除再插入。同样不能修改mapmultimap容器中元素的关键字

关联容器一般用平衡二叉树实现。在关联容器中查找和插入元素的时间复杂度都是O(log n)。

下面介绍pair类模板,其定义如下:

template<class _T1, class _T2>
struct pair {
    _T1 first;
    _T2 second;
  //还有若干构造函数的定义略去
};

最主要的就是两个成员函数,其实就可以看作一个数对/元组。STL中还有函数模板make_pair功能是生成一个pair模板类对象,定义如下:

template <class T1, class T2>
pair<T1, T2> make_pair(T1 x, T2 y) {
  return (pair<T1, T2>(x, y));
}

pair模板类对象有以下几种创建方式。

pair<int, double> p1;
pair<string, int> p2("this", 20);
pair<int, int> p3(pair<char, char>('a', 'b'));
pair<int, string> p4 = make_pair(200, "hello");

关联容器一些成员函数的返回值,以及mapmultimap容器中的元素都是pair对象。

multiset

类模板的定义如下:

template <class Key, class Pred = less<Key>, class B = allocator<Key> >
class multiset {
    //...
};

三个类型参数一般考虑前两个即可,第一个指的是容器中每个元素都是Key类型的,第二个参数Pred用于指明容器中元素的排序规则,默认是less<Key>类型,也就是用<运算符来进行比较,所以需要对其进行适当重载。

当定义这样的容器对象:multiset<A> s;就等价于定义:multiset<int, less<A>, allocator<A> > s;

注意:为了避免有些老编译器识别为>>运算符,最好中间加个空格。

常用的成员函数如下:

  • iterator find(const T & val);:查找值为val的元素返回其迭代器,找不到则返回end()
  • iterator insert(const T & val);:将val插入容器并返回其迭代器。
  • void insert(iterator first, iterator last);:将区间[first, last)的元素插入容器。
  • int count(const T & val);:统计多少个元素的值为val。
  • iterator lower_bound(const T & val);:查找一个最大的位置it,使得[begin(), it)中所有的元素都比val小,所以可见返回的迭代器可能是end(),或者其值大于等于val。
  • iterator upper_bound(const T & val);:查找一个最小的位置it,使得[it, end())中所有的元素都比val大,所以可见返回的迭代器可能是end(),或者其值大于val。
  • pair<iterator, iterator> equal_range(const T & val);:同时求得lower_boundupper_bound
  • iterator erase(iterator it);:删除it指向的元素,如果是VS2010则返回后面的迭代器,如果是C++标准或Dev C++则不是。
  • iterator erase(iterator first, iterator last);:删除左闭右开的区间,如果是VS2010则返回后面的迭代器,如果是C++标准或Dev C++则不是。

注意:如果集合中有val值,则lower_boundupper_bound刚好构成一个左闭右开的区间,区间中的值都是val。

注意:用erase成员函数删除迭代器i所指向的元素后,该迭代器即告失效,不能再++i之类。

set

multiset差不多,其插入单个元素的成员函数的返回值为pair模板类对象x,如果x.second为true,则说明插入成功,此时x.first()就指向被插入元素的迭代器;如果为false则表明元素已经存在,插入失败,x.first()指向原有那个元素的迭代器。

multimap

类模板的定义如下:

template<class Key, class T, class Pred = less<Key>, class A = allocator<T> >
 class multimap {
   ...
   typedef pair<const Key, T> value_type;
   ...
 };

multimap中的元素都是pair模板类的对象,first成员变量是关键字,second成员变量是值。默认也用less<Key>比较大小,用到<运算符。multimap的成员函数与multiset的类似,只需将T改成Key即可,且注意insert()的参数是一个pair模板类对象,而不是一个值。

举例,定义一个Key类型为int,值类型为double,元素按从大到小排序的类型MYMAP可以这样写:

typedef multimap<int, double, greater<int> > MYMAP;

map

其定义和multimap类似,map还有成员函数(重载)operator[]

T & operator[](Key K);

该成员函数返回first值为K的元素的second部分的引用,其实就跟数组元素的表示方式差不多。注意:如果容器中没有元素的first值为K,则自动添加一个first值为K的元素,若该元素的second成员变量是一个对象,则用无参构造函数对其进行初始化。

容器适配器

容器适配器都有这三个成员函数:pushtoppop。其中top返回顶部(对stack)或者队头(对queue,priority_queue)的元素的引用。注意容器适配器没有迭代器,所以STL中的各种排序、查找、变序算法不适用。

stack的头文件为<stack>,queue和priority_queue的头文件为<queue>

三个特有成员函数原型如下:

  • void pop();
  • T & top();
  • void push(const T & x);

注意pop并不会返回被弹出的那个元素的引用,与数据结构课程中的不一样。

stack

类模板定义如下:

template<class T, class Cont = deque<T> >
class stack {
  ...
};

第二个类型参数表明默认情况下stack是用deque来实现的,虽说如此但是不能用deque的成员函数。pushtoppop都在栈顶作用。还可以具体指定用vector或list实现。

queue

类模板定义如下:

template<class T, class Cont = deque<T> >
class queue {
  ...
};

默认deque实现,也可以指定用list实现。

注意push作用在队尾,toppop作用在队头。

priority_queue

类模板定义如下:

template<class T, class Container = vector<T>, class Compare = less<T> >
class priority_queue {
  ...
};

优先队列用“堆排序”实现,内部并非完全有序,但是能确保默认情况下最大元素在队头。优先队列插入删除元素的时间复杂度都是O(log n)。如果指定排序规则为greater<T>则确保队头是最小元素。

toppop作用在队头

string类详解

概述

string对象可以看作是顺序容器,支持随机访问迭代器,可以用数组的访问方式[]访问具体第几个字符,可以直接用cin和cout输入输出。头文件<string>

该类有多个构造函数,用法如下:

string s1();
string s2("Hello");
string s3(4, 'K');  //s3 = "KKKK"
string s4("12345", 1, 3);  //s4 = "234", 即从下标1开始长度为3的子串

构造函数不能输入单个字符或者一个整型变量,但是可以用单个字符对string类对象进行赋值。比如:

string s1, s2;
s1 = "Hello";
s2 = 'K';

还可以用成员函数assign来进行赋值。

string s1("12345"), s2;
s2.assign(s1);  //"12345"
s2.assign(s1, 1, 2);  //"23"
s2.assign(4, 'K');  //"KKKK"
s2.assign("abcde", 2, 3);  //"cde"

成员函数lengthsize可以返回字符串的长度(不含最后的\0字符)。

可以使用++=运算符对string对象执行字符串的连接操作,也可以用append成员函数来向字符串后面添加内容,该成员函数也可以用上面提到的几种格式。

可以使用>>=之类的运算符比较string对象,也可以用成员函数compare

string s1("hello"), s2("hello world");
int n = s1.compare(s2);
n = s1.compare("Hello");
n = s1.compare(1, 2, "Hello");
n = s1.compare(1, 2, s2);
n = s1.compare(1, 2, "Hello", 1, 2);
n = s1.compare(1, 2, s2, 1, 2);

下面的四条都是指s1的从下标1开始长度为2的子串。如果返回值为0表示两字符串相等,如果小于0则当前字符串小,否则为大。可以视作当前ASCII值减去后面的ASCII值。

两个字符串还可以通过成员函数swap来交换内容,比如s1.swap(s2)

子串与查找

substr成员函数可以用来求子串(n, m),即从下标n开始长度m的子串。如果m省略或者m超过字符串的长度,则子串就一直到字符串结束。

string s1 = "this is ok";
string s2 = s1.substr(2, 4);  //s2 = "is i"
s2 = s1.substr(2);  //s2 = "is is ok"

若干查找子串和字符的成员函数如下。它们的返回值都是子串或字符在string对象中的位置(下标)。如果查不到则返回string::npos静态常量。

  • find:从前往后查找子串或字符出现的位置。
  • rfind:从后往前查找子串或字符出现的位置。
  • find_first_of:从前往后查找何处出现另一个字符串中包含的字符。
  • find_last_of:从后往前查找何处出现另一个字符串中包含的字符。
  • find_first_not_of:从前往后查找何处出现另一个字符串中没有包含的字符。
  • find_last_not_of:从后往前查找何处出现另一个字符串中没有包含的字符。

还可以输入第二个整型参数表示从该下标开始找。

string s1("Source Code");
int n;
if((n = s1.find_first_of("ceo")) != string::npos) {
  cout << n << " " << s1.substr(n);
}

//输出"1 ource Code"

成员函数replace可以对string对象中的子串进行替换,返回值为对象自身的引用。

string s1("Real Steel");
s1.replace(1, 3, "123456", 2, 4);  //得到"R3456 Steel"

也可以用成员函数erase删除对象中的子串。

string s("Real Steel");
s.erase(1, 3);  //删除子串(1, 3), "R Steel"
s.erase(5);  //删除下标5及后面的所有字符, "R Ste"

可以用成员函数insert插入字符串。

string s("Limitless");
s.insert(2, "123");  //"Li123mitless"

将string对象作为流处理

使用流对象istringstreamostringstream可以将string对象当作一个流作为输入输出。头文件为<sstream>

#include <iostream>
#include <string>
#include <sstream>
using namespace std;
int main() {
  string src("Avatar 123 5.2 Titanic K");
  istringstream iS(src);  //把字符串src作为输入流iS
  string s1, s2;
  int n;
  double d;
  char c;
  iS >> s1 >> n >> d >> s2 >> c;

  ostringstream oS;
  oS << s1 << endl << n << endl << d << endl << s2 << endl << c << endl;
  cout << oS.str();
  return 0;
}

输出为:

Avatar
123
5.2
Titanic
K

Le vent se lève, il faut tenter de vivre.