1.string容器
1.1string特性
说到string的特性,就不得不合char *类型的字符串对比:
Char*是一个指针,String是一个类
string封装了char*,管理这个字符串,是一个char*型的容器String封装了很多实用的成员方法
查找find,拷贝copy,删除delete,替换replace,插入insert不用考虑内存释放和越界
string管理char*所分配的内存。每一次string的复制,取值都由string类负责维护,不用担心复制越界或者取值越界等
string和char*可以相互转换吗?如果能,怎么转换?
答案是可以转换。string转char*通过string提供的c_str()方法
//string转char*
string str = "string";
const char* cstr = str.c_str();
//char* 转string
string sstr(s);
1.2 string常用API
string构造函数:
string();//创建一个空字符串 例如:string str;
string(const string &str);//使用一个string对象初始化另一个string对象
string(const char *s);//使用字符串s初始化
string(int n,char c);//使用n个字符c初始化
//例子
//默认构造函数
string s1;
//拷贝构造函数
string s2(s1);
string s2 = s1;
//带参数构造函数
char *str = "string";
string s3(str);
string s4(10,'a')
string基本赋值操作
string& operator=(const char * s);//char*类型字符串 赋值给当前字符串
string& operator=(const string &s);//把字符串s赋给当前的字符串
string& operator=(char c);//字符赋值给当前的字符串
string& assign(const char *s);//把字符串s赋给当前的字符串
string& assign(const char *s, int n);//把字符串s的前n个字符赋给当前的字符串
string& assign(const string &s);//把字符串s赋给当前字符串
string& assign(int n,char c);//用n个字符c赋给当前字符串
string& assign(const string &s,int start,int n);//将s从start开始n个字符赋给字符串
string存取字符操作(warning 面试)
char& operator[](int n);//通过[]方式取字符
char& at(int n);//通过at方法获取字符
//[] 访问方式访问越界的时候,不会抛异常,直接挂掉
//at 会抛出异常,可以被catch抓到
int size();//返回字符串长度
string s("abcdefg");
char c = s[1];
char c2 = s.at(1);
字符串拼接操作
string& operator+=(const string& str);//重载+=操作符
string& operator+=(const char * str);//
string& operator+=(const char c);
string& append(const char *s);//把字符串s拼接到当前字符串结尾
string& append(const char *s,int n);//把字符串s的前n个字符链接到当前字符串结尾
string& append(const string &s);//同operator+=()
string& append(const string &s,int pos,int n);//把字符串s中从pos开始的n个字符链接到当前字符串结尾
string& append(int n,char c);//在当前字符串结尾添加n个字符c
字符串查找和替换
//不存在的话返回-1
int find(char c, int pos = 0) const;//从pos开始查找字符c在当前字符串的位置
int find(const char *s, int pos = 0) const;//从pos开始查找字符串s在当前串中的位置
int find(const char *s, int pos, int n) const;//从pos开始查找字符串s中前n个字符在当前串中的位置
int find(const string &s, int pos = 0) const;//从pos开始查找字符串s在当前串中的位置
int rfind(char c, int pos = npos) const;//查找字符c最后一次出现的位置
int rfind(const char *s, int pos = npos) const;//查找s最后一次出现的位置,从pos开始查找
int rfind(const char *s, int pos = npos, int n) const;//从pos查找s的前n个字符最后一次位置
int rfind(const string &s,int pos = npos) const;//查找str最后一次位置,从pos开始查找
string& replace(int pos,int n,const string& str);//替换从pos开始n个字符为字符串str
string& replace(int pos,int n,const char* s);//替换从pos开始的n个字符为字符串s
string比较操作
/*
compare函数在>时返回1,<时返回-1,==时返回0
比较区分大小写,比较时参考字典顺序,排在前面的越小
大写的A比小写的a小
*/
int compare(const string &s) const;//与字符串s比较
int compare(const char *s) const;//与字符串s比较
string 子串
string substr(int pos = 0,int n = npos)const;//返回值pos开始的n个字符组成的字符串
string插入和删除操作
string& insert(int pos,const char *s);//插入字符串
string& insert(int pos,const string& str);//插入字符串
string& insert(int pos,int n,char c);//在指定位置插入n个字符c
string& erase(int pos,int n = npos);//删除从Pos开始的n个字符
练习
#include <iostream>
#include <string>
//1.判断邮箱有效性,是否包含@和. 并且.在@之后
//2.判断用户输入的用户名中是否包含除了小写字母之外字符
//3.判断用户输入的邮箱地址是否正确
int main()
{
string email = "asdffas@hotmail.com";
int pos1 = email.find('@');//不存在的话
int pos2 = email.find('.');
if (pos1 == -1 || pos2 == -1 )
{
cout<<"邮箱中不包含@或. !!!"<<endl;
return 0;
}
if ( pos1 > pos2)
{
cout<<"输入邮箱不合法"<<endl;
}
string username = email.substr(0,pos1);
string::iterator pStar = username.begin();
string::iterator pEnd = username.end();
while(pStar != pEnd){
if (!(*pStar >= 'a' && *pStar <= 'z')){
cout<<"输入字符包含非小写字母"<<endl;
return 0;
}
pStar++;
}
string rightEmail = "zhaosi@itcast.cn";
int ret = email.compare(rightEmail);
if (ret != 0)
{
cout<<"邮箱地址不存在"<<endl;
return 0;
}
cout<<"邮箱地址正确"<<endl;
return 0;
}
2.vector容器
初始化
vector<T> v;//采用模板实现类,默认构造函数
vector(v.begin(),v.end());//将v[begin(),end()]区间中的元素拷贝给本身
vector(n,elem);//构造函数将n个elem拷贝给本身
vector(const vector &vec);//拷贝构造函数
----------------------------------------------------------
//例子 使用第二个构造函数 我们可以
int arr[] = {2,3,4,1,9};
vector<int> v1(arr,arr+sizeof(arr)/sizeof(int));//就是指针!!!!!
vector<int> v;
vector<int> v2(10,5);
vector<int> v3(v2.begin(),v2.end());
vector<int> v4(v3);
常用赋值方法
void assign(input_iterator beg,input_iterator end);//将[beg,end]区间中的数据拷贝赋值给本身
void assign(n,elem);//将n个elem拷贝赋值给本身
vector & operator=(const vector &vec);//重载等号操作符
void swap(vec);//swap()函数交换当前vector与vector from的元素
//第一个赋值函数,可以这么写
int arr[] = {0,1,2,3,4,5};
vector<int> v1;
v1 = assign(arr,arr+5);//使用数组初始化v1
vector<int> v2;
v2 = assign(v1.begin(),v1.end());
大小操作
size_type size();//返回容器中元素的个数
bool empty();//判断容器是否为空
void resize(int num);//重新制定容器的长度为num,若容器变长,则以默认值填充新位置。
//如果容器变短,则末尾超出容器长度的元素被删除
size_type capacity();//返回容器的容量
void reserve(int len);//容器预留len个元素长度,预留位置不初始化,元素不可访问
注意:resize若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除
数据存取操作
TYPE at(int idx);//返回索引idx所指的数据,如果idx越界,抛出out_of_range异常
operator[];//返回索引idx所指的元素,越界时,运行直接报错
TYPE front();//返回容器中的第一个数据元素
TYPE back();//返回容器中的最后一个数据元素
插入和删除操作
iterator insert(iterator loc,const TYPE &val);//在指定位置loc前插入值为val的元素,返回指向这个元素的迭代器
void insert(iterator loc,size_type num,const TYPE &val);//在指定位置loc前插入num个值为val的元素
void insert(iterator pos,input_iterator start,input_iterator end);//在指定位置loc前插入区间[start, end)的所有元素 .
void push_back(elem);//尾部插入元素elem
void pop_back();//删除最后一个元素
iterator erase(const_iterator start,const_iterator end);//删除迭代器从start到end之间的元素
iterator erase(const_iterator pos);//删除迭代器指向的元素
void clear();//删除容器中的所有元素
总结
vector是个动态数组,当空间不足的时候插入新元素,vector会重新申请一块更大的内存空间,将旧空间数据拷贝到新空间,然后释放旧空间。vector是单口容器,所以在尾端插入和删除元素效率较高
1 发现空间不足,重新申请一块更大的内存
2 将旧空间的数据拷贝新空间
3 旧空间释放掉
#include <iostream>
#include <vector>
int main()
{
int count;
int *p = NULL;
vector<int> v;
//因为频繁的申请内存,会造成效率降低
//所以我们可以用reserve()函数预先申请内存空间,这样容器就不需要再申请空间了
v.reserve(100000);
for(int i = 0;i<100000;i++){
v.push_back(i);
if (p != &v[0])//记录地址重新分配次数
{
count++;
p = &v[0];
}
}
cout<<"count:"<<count<<endl;//30 //1
cout<<"容量:"<<v.capacity()<<endl;//看容量138255 //100000
cout<<"大小:"<<v.size()<<endl;//100000 //100000
return 0;
}
3.deque容器
3.1deque特性
deque是“double-ended queue”的缩写,和vector一样,deque也支持随机存取。vector是单向开口的连续性空间,deque则是一种双向开口的连续性空间,所谓双向开口,意思是可以在头尾两端分别做元素的插入和删除操作,vector当然也可以在头尾两端进行插入和删除操作,但是头部插入和删除操作效率奇差,无法被接受
3.2deque常用API
deque构造函数
deque<T> deqT;//默认构造函数
deque(iterator beg,iterator end);//构造函数将[beg,end)区间中的元素拷贝给自身
deque(int num,const TYPE elem);//构造函数将n个elem拷贝给自身
deque(const deque &deq);//拷贝构造函数
deque赋值操作
void assign(iterator beg,iterator end);//将[beg,end)区间中的数据拷贝赋值给本身
void assign(int num,const TYPE &elem);//将n个elem拷贝赋值给本身
deque & operator=(const deque &deq);//重载等号操作符
void swap(deq);//将deq与本身的元素互换
deque大小操作
deque.size();//返回容器中元素的个数
deque.empty();//判断容器是否为空
deque.resize(num);//重新制定容器的长度为Num,若容器边长,则以默认值填充新位置。
//如果容器变短,则末尾超出容器长度的元素被删除
deque.resize(num,const TYPE &elem);//重新制定容器的长度为Num,若容器边长,则以elem填充新位置。
//如果容器变短,则末尾超出容器长度的元素被删除
deque双端插入和删除操作
void push_back(const TYPE &elem);//在容器尾部添加一个数据
void push_front(const TYPE &elem);//在容器头部插入一个数据
void pop_back();//删除容器最后一个数据
void pop_front();//删除容器第一个数据
deque数据存取
TYPE at(int index);//返回索引idex所指的数据,如果idx越界,会抛出out_of_range
TYPE operator [](int index);//返回索引idx所指的数据,如果idx越界,不抛出异常,直接出错
TYPE front();//返回第一个数据
TYPE back();//返回最后一个数据
deque插入操作
int insert(iterator pos,const TYPE &val);//在pos位置插入一个elem元素的拷贝,返回新数据的位置
iterator insert( iterator pos, size_type num, const TYPE &val );//在Pos位置插入num个elem元素
void insert( iterator pos, input_iterator start, input_iterator end );//在pos位置插入[beg,end)区间的数据
4.练习
#warning 对push_back()函数有理解!!!
#include <iostream>
#include <deque>
#include <string>
#include <vector>
#include <algorithm>
//评委打分案例(sort算法排序)
//创建5个选手(姓名,得分),10个评委对5个选手进行打分
//得分规则:去除最高分,去除最低分,取出平均分
//按得分对5名选手进行排名
//选手类
class Player
{
public:
Player(string name,int score){
this->name = name;
this->score = score;
cout<<"Player()...."<<endl;
}
~Player(){
cout<<"~Player()...."<<endl;
}
string name;
int score;
private:
}
//创建选手
void Create_Player(vector<Player> &v)
{
string name_seed = "ABCDE";
for (int i = 0; i < 5; ++i)
{
string name = "选手";
name.append(name_seed.at[i]);
Player p(name,0);//创建选手
v.push_back(p);
}
}
//打分
bool mycompare(int v1,int v2)
{
if (v1 > v2)//从大到小
{
return true;
}
return false;
}
void Set_Player_Score(vector<Player>& plist)
{
for(vector<Player>::iterator it = plist.begin(); it != plist.end();it++){
deque<int> dscore;
for(int i = 0; i < 10;i++){
int score = 50 + rand() % 50;//打分
dscore.push_back(score);
}
//排序 sort 算法
sort(dscore.begin(),dscore.end(),mycompare);
dscore.pop_back();//去除最低分
dscore.pop_front();//去除最高分
int totalscore = 0;
for(deque<int>::iterator dt = dscore.begin(); dt != dscore.end();dt++){
totalscore += *dt;
}
int score_avg = totalscore / dscore.size();
(*it).score = score_avg;
}
}
//显示选手得分
void Show_Player_Score(vector<Player> &plist)
{
for(vector<Player>::iterator it = plist.begin(); it != plist.end();it++){
cout<<"姓名:"<<(*it).name<<" "<<"分数:"<<(*it).score<<endl;
}
}
int main()
{
vector<Player> vPlayer;//存放选手信息
Create_Player(vPlayer);
Set_Player_Score(vPlayer);
Show_Player_Score(vPlayer);
return 0;
}