二、常用容器(String、Vector、Deque)

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容器

1.png

初始化

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是单口容器,所以在尾端插入和删除元素效率较高

size()和capacity()的区别
部分区别解释

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当然也可以在头尾两端进行插入和删除操作,但是头部插入和删除操作效率奇差,无法被接受

2.png
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;
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容

  • STL(标准模板库),是目前C++内置支持的library。它的底层利用了C++类模板和函数模板的机制,由三大部分...
    岁与禾阅读 38,952评论 3 133
  • 前言: 详细介绍: List:元素有放入顺序,元素可重复Map:元素按键值对存储,无放入顺序Set:元素无放入顺序...
    YBshone阅读 8,616评论 0 17
  • 容器的概念所谓STL容器,即是将最常运用的一些数据结构(data structures)实现出来。容器是指容纳特定...
    饭饭H阅读 374评论 0 0
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,567评论 18 399
  • 本文由币乎社区(bihu.com)内容支持计划奖励 当前,全球新一轮科技革命和产业变革持续深入,区块链在其中扮...
    区块链台灯阅读 1,164评论 0 0