String 类
string类是一个模板类:
typedef basic_string< char > string
使用string类要包含头文件< string>
string 对象的初始化:
- string s1(“hello”);
- string month=”match”;
- string s2(8,’x’);
- 等。。。。
#include <iostream>
#include <string>
using namespace std;
int main(){
string s1("hello");
cout<<s1<<endl;
string s2(8, 'x');
cout<<s2<<endl;
string match = "matrch";
cout<<match<<endl;
string s;
s = 'n';
cout<<s<<endl;
return 0;
}
hello
xxxxxxxx
matrch
n
string支持getline函数
- string s;
- getline(cin,s)
string的赋值和连接
- 用=赋值
- string s1(“cat”), s2
- s2=s1
- 用assign成员函数赋值
- string s1(“cat”), s2
- s2.assign(s1);
- 用assign成员函数部分复制
- string s1(“catpig”), s3;
- s3.assign(s1,1,3); //下标为1到3这3个元素赋值给s3
- 单个字符赋值
- 单个字符复制:s2[5]=s1[3]=’a’;
- 逐个访问string中的字符string s1(“hello”) s1.at(i)
- 用+运算符连接字符串
- string s1(“good”), s2(“morning!”);
- s1+=s2
- 用成员函数append连接字符串
- string s1(“good”), s2(“morning”)
- s1.append(s2)
- s1.append(s1, 3, s1.size()) //下标为3的位置开始,赋值s1.size()个字符,如果没用那么多字符,就赋值到最后一个字符
- 用<,>,==比较字典序
- 用成员函数compare比较string的大小
- s1.compare(1,2,s3,0,3)是说,用s1的下标为1到2(包括2)的字符和s3的0到3的字符比较
子串
string s1(“hello world”), s2;
s2 = s1.substr(4,5); //从下标为4的位置,向后数5个字符进行赋值
交换两个string: s1.swap(s2)
寻找string中的字符
- 成员函数 find()和rfind()
- string s1(“hello world”)
- s1.find(“lo”)
- 在s1中从前向后查找,”lo”第一次出现的地方,如果找到,返回”lo”开始的下标(“l”的下标) 如果找不到,返回string::npos(string中定义的静态常量)
- s1.find(“lo”,1)//从下标为1的地方开始找
- find_first_of() 和find_last_of()(这个是最后一次出现的地方)
- string s1(“hello world”);
- s1.find_first_of(“abcd”);
- 在s1中从前向后找”abcd”中任何一个字符第一次出现的地方,如果找到,返回找到字母的位置,找不到则返回string::npos。
- 成员函数find_first_not_of() 和find_last_not_of()
- string s1(“hello world”)
- s1.find_first_not_of(“abcd”);
- 在s1中从前向后查找不在,“abcd”中的字母第一次出现的地方,如果找到,返回找到字母的位置,如果找不到,返回string::npos。
- 删除string中的字符
- 成员函数erase()
- string s1(“hello world”)
- s1.erase(5) //删除5以及之后的字符
- 替换string中的字串
- 成员函数replace()
- string s1(“hello world”)
- s1.replace(2,3,”haha”) // 将s1中下标2开始的3个字符换成”haha”
- s1.replace(2,3,”haha”,1,2) // 将s1中下标2开始的3个字符换成“haha”中下标1开始的2个字符
- 在string中插入字符
- 成员函数insert()
- string s1(“hello world”)
- string s2(“show insert”)
- s1.insert(5, s2)
- s1.insert(2, s2, 5, 3)
- 转换成C语言式char *字符串
- 成员函数c_str()
- string s1(“hello world”)
- printf(“%s\n”,s1.c_str()) // s1.c_str() 返回传统的const char*类型字符串,该字符串以 ‘\0’ 结尾
- 转换成c语言式char*字符串
- 成员函数data()
- string s1(“hello world”)
- const char *p1=s1.data()
- for(int i=0;i
字符串流处理
除了标准流和文件流输入输出之外,还可以从string进行输入输出
类似istream和ostream进行标准流输入输出,我们用istringstream和ostringstream进行字符串上的输入输出,也称为内存输入输出:include< sstream>
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int main(){
string s1("hello world");
string ss1, ss2;
istringstream inputstring(s1);
inputstring>>ss1>>ss2;
cout<<ss1<<" "<<ss2;
return 0;
}
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int main(){
string s1("hello world");
string ss1, ss2;
istringstream inputstring(s1);
inputstring>>ss1>>ss2;
ostringstream out;
out<<"the "<<22<<" ok"<<endl;
cout<<out.str();
cout<<"there";
return 0;
}
the 22 ok
there
标准模板库(STL)概述
泛型程序设计
c++语言的核心优势之一就是便于软件的重用
c++中有两个方面体现重用,
- 面向对象的思想:继承和多态,标准类库
- 泛型程序设计的思想:模板机制以及标准模板库STL
STL中的基本的概念
- 容器:可容纳各种数据类型的通用数据结构,是类模板
- 迭代器:可用于一次存取容器中元素,类似于指针
- 算法:用来操作容器中哦的元素的函数模板
- sort() 来对一个vector中的数据进行排序
- find()来搜索一个list中的对象
- 算法本身与他们操作的数据的类型无关,因此他们可以在从简单数组到高度复杂容器的任何数据结构上使用
容器概述:可以用于存放各种类型的数据(基本数据类型的变量,对象等)的数据结构,都是类模板,分为三种:
- 顺序容器:vector, deque, list
- 关联容器:set, multiset, map, multimap
- 容器适配器:stack, queue, priority_queue
顺序容器简介
vector:动态数组,元素在内存连续存放。
deque:头文件< deuqe>双向队列。元素在内存连续存放。随机存取任何元素都能在常数时间内完成(但次于vector)。在两端增删元素具有较佳的性能(大部分情况下是常数时间)。
list:头文件< list>双向链表。元素在内存不连续存放。在任何位置增删元素都能在常数时间内完成。不支持随机存取。
关联容器简介
- 元素是排序的
- 插入任何元素,都按相应的排序规则来确定其位置
- 在查找时具有
- 通常以平衡二叉树方式实现,插入和检索时间都是O(log(N))
- set/multiset 头文件< set> set即集合。set中不允许相同元素,multiset中允许存在相同元素。
- map/multimap 头文件< map>
- map和set的不同在于map中存放的元素有且仅有两个成员变量,一个名为first,另一个名为second,map根据first值对元素进行从小到大排序,并可快速地根据first来检索元素。map同multimap的不同在于是否允许相同first值的元素。
容器适配器简介
- stack:头文件:< stack> 栈。是项的有限序列,并满足序列中被删除、检索和修改的项只能是最近插入序列的项(栈顶的项)。后进先出
- queue:头文件< queue>队列。插入只可以在尾部进行,删除、检索和修改只允许从头部进行。先进先出。
- priority_queue:头文件< queue> 优先级队列。最高优先级元素总是第一个出列
顺序容器和关联容器中都有哪些成员函数
- begin: 返回指向容器中第一个元素的迭代器
- end: 返回指向容器中最后一个元素后面位置的迭代器
- rbegin:返回指向容器中最后一个元素的迭代器
- rend: 返回指向容器中第一个元素前面的位置的迭代器
- erase: 从容器中删除一个或几个元素
- clear: 从容器中删除所有元素
顺序容器的常用的成员函数
- front: 返回容器中第一个元素的引用
- back: 返回容器中最后一个元素的引用
- push_back: 在容器末尾增加新元素
- pop_back: 删除容器末尾的元素
- erase:删除迭代器指向的元素(可能会使该迭代器失效),或删除一个区间,返回被删除元素后面的那个元素的迭代器
标准模板库STL概述(二)
迭代器
- 用于指向顺序容器和关联容器中的元素
- 迭代器用法和指针类似
- 有const和非const两种
- 通过迭代器可以读取它指向的元素
- 通过非const迭代器还能修改其指向的元素
定义迭代器的方法
- 容器类名::iterator 变量名;
- 容器类名::const_iterator 变量名;
- 访问一个迭代器指向的元素:*迭代器变量名
迭代器上可以执行++操作,以使其指向容器中的下一个元素。如果到达了最后一个元素后面,就会出错。
#include <vector>
#include <iostream>
using namespace std;
int main()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
vector<int>::const_iterator i;
for (i = v.begin(); i != v.end(); ++i) {
cout<<*i<<",";
}
cout<<endl;
vector<int>::reverse_iterator r;//反向迭代器,这里r++会指向容器中的前一个元素
for (r = v.rbegin(); r!=v.rend(); ++r) {
cout<<*r<<",";
}
vector<int>::iterator j; //非常量迭代器
for (j = v.begin(); j != v.end() ; ++j) {
*j=100;
}
cout<<endl;
for(auto x:v){
cout<<x<<",";
}
}
1,2,3,4,
4,3,2,1,
100,100,100,100,
双向迭代器
- ++p, p++, –p, p–
- *p //返回那个值(的引用)
- p=p1
- p==p1, p!=p1
随机访问迭代器
- 双向迭代器的所有操作
- p+=i
- p-=i
- p+i, p-i, p[i],
- p
容器和容器上迭代器的种类
- vector 随机访问
- deque 随机访问
- list 双向
- set/multiset 双向
- map/multimap 双向
- stack 不支持迭代器
- queue 不支持迭代器
- priority_queue 不支持迭代器
有的容器,例如sort和binary_search需要通过随机访问迭代器来访问容器中的元素,那么list以及关联容器就不支持改算法
vector 的迭代器是随机迭代器
list是双向迭代器,用法如下:
list<int> vv;
vv.push_back(1);
vv.push_back(3);
vv.push_back(5);
list<int> ::const_iterator ii;
//这里不能用ii<vv.end(),不能用vv[i]
for (ii=vv.begin();ii!=vv.end();++ii){
cout<<*ii<<",";
}
算法简介
- 算法就是一个个函数模板,大多是在< algorithm>中定义
- STL中提供能在各种容器中通用的算法,比如查找、排序等
- 算法通过迭代器来操纵容器中的元素,许多算法可以对容器中的一个局部区间进行操作,因此需要两个参数,一个是起始元素的迭代器,一个是终止元素的后面一个元素的迭代器
- 有的算法返回一个迭代器。比如find()算法,在容器中查找一个元素,并返回一个指向该元素的迭代器
- 算法可以处理容器,也可以处理普通数组
算法示例 find()
- template
STL中“大”,“小”的概念
- 关联容器内部的元素是从小到大排序的
- 有些算法要求其操作的区间是从小到大排序的,称为“有序区间算法”。例如:binary_search
- 有些算法会对区间进行从小到大排序,成为排序算法。例如:sort
- 还有一些其他算法会用到“大”,“小”的概念
- 使用STL时,在缺省的情况下,以下三个说法等价:x比y小,x
#include <iostream>
#include <algorithm>
using namespace std;
class A{
int v;
public:
A(int n):v(n){}
bool operator < (const A &a2)const{
cout<<v<<"<"<<a2.v<<"?"<<endl;
return false;
}
bool operator == (const A &a2) const{
cout<<v<<"=="<<a2.v<<"?"<<endl;
return v==a2.v;
}
};
int main()
{
A a[]={A(1), A(2), A(3), A(4), A(5)};
cout<<binary_search(a, a+4, A(9));//折半查找中,只是判断了是否小于。。返回1是找到了
return 0;
}
3<9?
2<9?
1<9?
9<1?
1
vector 和deque
vector
#include <iostream>
#include <vector>
using namespace std;
template <class T>
void PrintVector(T s, T e){
for(;s!=e;++s)
cout<<*s<<" ";
cout<<endl;
}
int main(){
int a[5]={1,2,3,4,5};
vector<int> v(a, a+5);
cout<<"1)"<<v.end()-v.begin()<<endl;
cout<<"2)";
PrintVector(v.begin(), v.end());
v.insert(v.begin()+2, 13);//插入。。第一个参数是位置,第二个位置是元素
cout<<"3)";
PrintVector(v.begin(), v.end());
v.erase(v.begin()+2);//删除
cout<<"4)";
PrintVector(v.begin(), v.end());
vector<int> v2(4, 100);//4个元素,每个元素都是100
v2.insert(v2.begin(),v.begin()+1, v.begin()+3);//在v2里面插入某个区间
cout<<"5) v2:";
PrintVector(v2.begin(), v2.end());
v.erase(v.begin()+1, v.begin()+3);
cout<<"6) ";
PrintVector(v.begin(), v.end());
return 0;
}
1)5
2)1 2 3 4 5
3)1 2 13 3 4 5
4)1 2 3 4 5
5) v2:2 3 100 100 100 100
6) 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 < 4; ++j) {
cout<<v[i][j]<<" ";
}
cout<<endl;
}
return 0;
}
0 1 2 3
0 1 2 3
0 1 2 3
deque:适合vector的操作都适用于deque。
deque还要push_front(将元素插入到前面)和pop_front(删除最前面的元素)操作,复杂度是O(1)
双向链表:list
- 在任何位置插入删除都是常数时间,不支持随机存取
- 除了具有所有容器都有的成员函数以外,还支持8个成员函数:
- push_front: 在前面插入
- pop_front: 删除前面的元素
- sort:排序(list不支持STL的算法sort)
- remove: 删除和指定值相同的所有元素
- unique: 删除所有和前一个元素相同的元素(要做到元素不重复,则unique之前还需要sort)
- merge:合并两个链表,并清空被合并的那个
- reverse:颠倒链表
- splice: 在指定位置前面插入另一个链表中的一个或多个元素,并在另一链表中删除被插入的元素
#include <list>
#include <iostream>
#include <algorithm>
using namespace std;
class A{
private:
int n;
public:
A(int _n):n(_n){}
friend bool operator<(const A &a1, const A &a2);
friend bool operator==(const A &a1, const A &a2);
friend ostream &operator<<(ostream &o, const A &a);
};
bool operator<(const A &a1, const A &a2) {
return a1.n<a2.n;
}
bool operator==(const A &a1, const A &a2) {
return a1.n==a2.n;
}
ostream& operator<<(ostream &o, const A &a) {
o<<a.n;
return o;
}
template <class T>
void PrintList(const list<T> & lst){
//不推荐的写法,用两个迭代器更好
typename list<T>::const_iterator i; //typename用来说明list<T>::const_iterator是个类型
i = lst.begin();
for (i = lst.begin(); i!=lst.end(); ++i) {
cout<<*i<<",";
}
}
int main()
{
list<A> lst1, lst2;
lst1.push_back(1);lst1.push_back(3);
lst1.push_back(2);lst1.push_back(4);
lst1.push_back(2);
lst2.push_back(10);
lst2.push_front(20);
lst2.push_back(30);
lst2.push_front(30);
lst2.push_back(30);
lst2.push_front(40);
lst2.push_back(40);
cout<<"1)";
PrintList(lst1);cout<<endl;
cout<<"2)";
PrintList(lst2);cout<<endl;
lst2.sort();
cout<<"3)";
PrintList(lst2);
cout<<endl;
lst2.pop_front();//删掉头部元素
cout<<"4)";
PrintList(lst2);
cout<<endl;
lst1.remove(2); //删除和A(2)相等的元素
cout<<"5)";
PrintList(lst1);
cout<<endl;
lst2.unique();
cout<<"6)";
PrintList(lst2);
cout<<endl;
lst1.merge(lst2); //合并lst2到lst1并清空lst2
cout<<"7)";
PrintList(lst1);
cout<<endl;
cout<<"8)";
PrintList(lst2);
cout<<endl;
lst1.reverse();
cout<<"9)";
PrintList(lst1);
cout<<endl;
lst2.push_back(100);
lst2.push_back(200);
lst2.push_back(300);
lst2.push_back(400);
list<A>::iterator p1, p2, p3;
p1 = find(lst1.begin(), lst1.end(), 3);
p2 = find(lst2.begin(), lst2.end(), 200);
p3 = find(lst2.begin(), lst2.end(), 400);
lst1.splice(p1, lst2, p2, p3);//将lst2[p2,p3)插入到p1之前, 并从lst2中删除[p2, p3)
cout<<"10)";
PrintList(lst1);
cout<<endl;
cout<<"11)";
PrintList(lst2);
cout<<endl;
return 0;
}
1)1,3,2,4,2,
2)40,30,20,10,30,30,40,
3)10,20,30,30,30,40,40,
4)20,30,30,30,40,40,
5)1,3,4,
6)20,30,40,
7)1,3,4,20,30,40,
8)
9)40,30,20,4,3,1,
10)40,30,20,4,200,300,3,1,
11)100,400,
函数对象:若一个类重载了运算符“()”,则该类的对象就成为函数对象
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <functional>
using namespace std;
int SumSquares(int total, int value)
{return total+value*value;}
template <class T>
void PrintInterval(T first, T last)
{
for(;first!=last;++first)
{
cout<<*first<<" ";
}
cout<<endl;
}
template <class T>
class SumPowers
{
private:
int power;
public:
SumPowers(int p):power(p){}
const T operator()(const T&total, const T&value){
T v=value;
for(int i=0;i<power-1;i++) v=v*value;
return total+v;
}
};
int main()
{
const int SIZE = 10;
int a1[]={1,2,3,4,5,6,7,8,9,10};
vector<int> v(a1, a1+SIZE);
cout<<"1)";
PrintInterval(v.begin(), v.end());
int result = accumulate(v.begin(), v.end(), 0, SumSquares);//SumSquares是个函数
/*
这里实例化了:
int accumulate(vector<int>::iterator first, vector<int>::iterator last, int init, int(*op)(int, int))
{
for(;first!=last;++first)
init=op(init, *first);
return init;
}
*/
cout<<"2)平方和"<<result<<endl;
//SumPowers是个类模板,SumPowers<int>模板类,SumPowers<int>(3)是个对象,这个对象重载了“()”
result = accumulate(v.begin(), v.end(), 0, SumPowers<int>(3));
/*
int accumulate(vector<int>::iterator first, vector<int>::iterator last, int init, SumPowers<int> op)
{
for(;first!=last;++first)
init=op(init, *first);
return init;
}
*/
cout<<"3)立方和"<<result<<endl;
result=accumulate(v.begin(), v.end(), 0, SumPowers<int>(4));
cout<<"4)4次方和"<<result<<endl;
return 0;
}
1)1 2 3 4 5 6 7 8 9 10
2)平方和385
3)立方和3025
4)4次方和25333
//在c++源码中如下:
template<typename _InputIterator, typename _Tp>
inline _Tp
accumulate(_InputIterator __first, _InputIterator __last, _Tp __init)
{
// concept requirements
__glibcxx_function_requires(_InputIteratorConcept<_InputIterator>)
__glibcxx_requires_valid_range(__first, __last);
for (; __first != __last; ++__first)
__init = __init + *__first;
return __init;
}
template<typename _InputIterator, typename _Tp, typename _BinaryOperation>
inline _Tp
accumulate(_InputIterator __first, _InputIterator __last, _Tp __init,
_BinaryOperation __binary_op)
{
// concept requirements
__glibcxx_function_requires(_InputIteratorConcept<_InputIterator>)
__glibcxx_requires_valid_range(__first, __last);
for (; __first != __last; ++__first)
__init = __binary_op(__init, *__first);
return __init;
}
STL中的函数对象类模板:头文件:< functional>
greater 函数对象类模板
//在c++中源码如下:
template<typename _Tp>
struct greater : public binary_function<_Tp, _Tp, bool>
{
_GLIBCXX14_CONSTEXPR
bool
operator()(const _Tp& __x, const _Tp& __y) const
{ return __x > __y; }
};
greater的应用
list有两个sort成员函数
- void sort(); 将list中的元素按”<”规定的比较方法升序排列
- template< class Compare>
- void sort(Compare op); 将list中的元素按op规定的比较方法升序排列。即要比较x, y大小时,看op(x, y)的返回值,为true则认为x小于y
#include <list>
#include <iostream>
using namespace std;
class MyLess{
public:
bool operator()(const int &c1, const int &c2)
{
//比较各位数
return (c1%10)<(c2%10);
}
};
template <class T>
void Print(T first, T last){
for(;first!=last;++first) cout<<*first<<",";
}
int main()
{
const int SIZE=5;
int a[SIZE]={5,21,14,2,3};
list<int> lst(a, a+SIZE);
lst.sort(MyLess());
Print(lst.begin(), lst.end());
cout<<endl;
lst.sort(greater<int>());//greater<int>()是个对象
Print(lst.begin(), lst.end());
cout<<endl;
return 0;
}
21,2,3,14,5,
21,14,5,3,2,
在STL中使用自定义的“大”,“小”关系
关联容器和STL中许多算法,都是可以用函数或函数对象自定义比较器的。在自定义了比较器op的情况下,以下三个说法是等价的:1.x小于y;2.op(x,y) 返回true;3.y大于x
#include <iostream>
#include <iterator>
using namespace std;
class MyLess{
public:
bool operator()(int a1, int a2){
if((a1%10)<(a2%10)) return true;
else return false;
}
};
bool MyCompare(int a1, int a2){
if((a1%10)<(a2%10)) return false;
else return true;
}
template <class T, class pred>
T MyMax(T first, T last, pred op){
T temp = first;
for(;first!=last;++first){
if(op(*temp, *first)) 如果*temp小于*first
temp = first;
}
return temp;
};
int main()
{
int a[]={35, 7,13, 19, 12};
cout<<*MyMax(a, a+5, MyLess())<<endl;
cout<<*MyMax(a, a+5, MyCompare)<<endl;
return 0;
}
19
12