一.string容器

1.string理解

string是C++中风格的字符串,在C语言中并没有string的内置数据类型,这是因为string的本质是一个类。

我们知道在C语言中我们使用char []来存储一个字符串,其实也就是char *来管理字符串,他的本质是一个指针。

C++中string是一个类,类内部封装了char *的成员属性,管理这个字符串,是一个char *型的容器。

特点:

  • string 类内部封装了很多成员方法:
    例如: 查找find,拷贝copy,删除delete 替换replace,插入insert
  • string管理char*所分配的内存,不用担心复制越界和取值越界等,由类内部进行负责

2.string构造函数

string();                                       //无参构造,创建一个空字符串
string(const char* s);                          //把C语言风格的字符串转换成C++风格
string(const string& str);                      //拷贝构造
string(int n,char c);                           //用n个字符c初始化

string构造函数灵活多用,根据实际情况选择。

3.字符串赋值操作

string& operator=(const char* s);                    //把char*类型字符串赋值给当前字符串
string& operator=(const string &s);                  //类似于拷贝构造
string& operator=(char c);                           //可以把一个字符赋值给当前字符串
string& assign(const char*s);                        //成员函数,赋值
string& assign(const char*s,int n);                  //成员函数重载,把字符串s前n个字符赋值给当前字符串
string& assign(const string &s);                     //成员函数重载,类似于拷贝构造
string& assign(int n,char c);                        //用n个字符c赋值给当前字符串

上面部分基于运算符=重载,下面基于叫assign的成员函数。

string的赋值方式很多,operator= 这种方式是比较实用的。

4.字符串拼接

string& operator+=(const char* str);                          //重载+=,当前字符串后拼接一个C语言风格字符串
string& operator+=(const char c);                             //重载+=,当前字符串后可以拼接单个字符
string& operator+=(const string& str);                        //重载+=,当前字符串后面拼接另一个字符串
string& append(const char* s);                                //成员函数,当前字符串后拼接一个C语言风格字符串
string& append(const char* s,int n);                          //成员函数,把字符串s前n个字符连接到当前字符串后面
string& append(const string &s);                              //成员函数,当前字符串后面拼接另一个字符串
string& append(const string &s,int pos,int n);                //成员函数,把字符串s从pos开始的n个字符连接到当前字符串结尾

上面部分基于运算符+=重载,下面基于叫append的成员函数。

5.字符串查找和替换

int find(const string& str,int pos=0) const;                   //查找str第一次出现的位置,从pos开始查找
int find(const char* s,int pos=0) const;                       //查找s第一次出现的位置,从pos开始查找
int find(const char* s,int pos,int n)const;                    //从pos位置查找s的前n个字符第一次出现的位置
int find(const char c,int pos=0) const;                        //查找字符c出现的第一次位置
int rfind(const string& str,int pos=npos) const;               //查找str最后一次出现位置,从pos开始查找
int rfind(const char* s,int pos=npos) const;                   //查找s最后一次出现位置,从pos开始查找
int rfind(const char* s,int pos,int n) const;                  //从pos查找s的前n个字符最后一次位置
int rfind(const char c,int pos=0) const;                       //查找字符c最后一次出现的位置
string& replace(int pos,int n,const string& str);              //替换从pos开始的n个字符为字符串str
string& replace(int pos,int n,const char* s );                 //替换从pos开始的n个字符为字符串s

1.参数列表中int pos=0是默认参数的形式;

2.findrfind的区别:find是从左到右查找,而rfind是从右到左查找,从右到左的第一次相较于正常顺序(从左到右)也就是最后一次;

3.findrfind的返回值都是int类型,表示下标,下标从0开始,若字符串中没有找到相关字符串,返回-1;

4.replace替换,要么用C++风格字符串string替换,要么用C语言风格字符串char *替换。

6.字符串比较

比较方式:遍历每个字符,按ASCll码表的值进行比较

  • =,返回0
  • >,返回1
  • <,返回-1
int compare(const string& s) const;        //与字符串比较
int compare(const char* s) const;          //与字符串比较

1.只有字符串每个字符对应的ASCll码表对应的值相等,才会等于

2.字符串比较一般只用来判断两个字符串是否相等,比较其大小没有什么意义

7.字符存取

char& operator[](int n);              //通过[]方式获取字符
char& at(int n);                      //通过成员函数获取字符

单个字符的存取,即对字符串中某个字符进行读或写的操作。

8.插入和删除

string& insert(int pos,const char* s);                   //从pos位置开始插入字符串s
string& insert(int pos,const string& str);               //从pos位置开始插入字符串str
string& insert(int pos,int n,char c);                    //在指定位置插入n个字符c
string& erase(int pos,int n=npos);                       //删除从pos开始的n个字符

插入和删除下标都是从0开始。

9.子串

string substr(int pos=0,int n=npos) const;            //返回由pos开始的n个字符组成的字符串

二.vector容器

1.vector理解

vector数据结构与数组非常相似,也被称为单端数组

vector与普通数组的区别:数组是静态空间,而vector可以动态扩展

动态扩展:并不是在原空间之后续接新空间,而是找更大的内存空间,然后将原数据拷贝到新空间,释放原空间

从C向C++15——常见容器1_字符串

vector容器的迭代器是支持随机访问的迭代器

2.vector构造函数

vector<T> v;                   //采用模板实现类实现,默认构造函数
vector v_1(v.begin(),v.end());     //将v[begin(),end()]区间中的元素拷贝给当前v_1容器,区间构造
vector(n,elem);                //将n个elem元素拷贝给当前容器
vector(const vector &vec);     //拷贝构造函数

进行一下练习:

#include <iostream>
#include <vector>
using namespace std;

template<typename T>
void print(vector<T> v)
{
    for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
        cout << *it <<" ";
    cout << endl;
}


void test01()
{
    //第一种构造
    vector<int> v1;
    for (int i = 0; i < 10; i++)
        v1.push_back(i);
    print(v1);

    //第二种区间构造
    vector<int> v2(v1.begin(), v1.end());
    print(v2);

    //第三种构造
    vector<int> v3(10, 2);
    print(v3);

    //第四种构造:拷贝构造
    vector<int> v4(v3);
    print(v4);
}

int main()
{
    test01();
    return 0;
}

3.vector赋值操作

vector& operator=(const vector &vec);                   //重载等号操作符,类似于拷贝构造
assign(beg,end);                                        //类似于区间拷贝构造
assign(n,elem);                                         //将n个elem拷贝赋值给当前对象

重载=或成员函数assign()完成赋值操作,但一般我们在构造的时候就会完成赋值操作。

4.vector容量与大小

empty();                         //判断容器是否为空
capacity();                      //返回容器的容量
size();                          //返回容器中元素的个数
resize(int num);                 //重新指定容器的大小为num,若容器变长,则以默认值0填充新位置
                                 //如果容器变短,则末尾超出容器大小的元素被删除
resize(int num,elem);            //重新指定容器的大小为num,若容器变长,则以元素elem填充新位置
                                 //如果容器变短,则末尾超出容器大小的元素被删除

5.vector插入和删除

push_back(ele);                               //尾部插入元素ele
pop_back();                                   //删除最后一个元素
insert(const_iterator pos,ele);               //迭代器指定位置pos插入元素ele
insert(const_iterator pos,int count,ele);     //迭代器指定位置pos插入count个元素ele
erase(const_iterator pos);                    //删除迭代器指向的元素
erase(const_iterator start,const_iterator end);   //删除迭代器从start到end之间的元素
clear();                                      //删除容器中的所有元素

注意:插入和删除的参数是一个迭代器,可以简单的把迭代器理解为一个广义指针。

6.vector数据存取

at(int idx);                  //成员函数返回索引idx所指的数据
operator[idx];                //重载[]返回索引idx所指的数据
front();                      //返回容器中的第一个数据元素
back();                       //返回容器中的最后一个数据元素

除了用迭代器访问容器vector中的元素,[]at()也可以实现数据存取。

7.vector互换容器

swap(vec);                   //将容器vec与自身元素交换

交换元素很好理解,但我们需要知道它的一般用途:实际上交换函数往往用来收缩空间使用。

比如现在有一个容器v的大小1000,根据系统动态扩展,其容量可能为1300,而现在如果我们进行resize重新设置大小为3,但其容量并不会减少,所以我们需要用该函数来进行收缩内存空间,原理是与一个匿名对象交换元素,这样操作后原来v的容量和大小都变成了3:

vector<int>(v).swap(v);                //与匿名对象交换元素

8.vector预留空间

目标:减少vector动态扩展的次数

reverse(int len);                    //预留len的内存容量,预留位置不初始化,元素不可访问

练习:

#include <iostream>
#include <vector>
using namespace std;

template<typename T>
void print(vector<T> v)
{
    for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
        cout << *it <<" ";
    cout << endl;
}


void test01()
{
    //第一种构造

    int num = 0;//表示动态扩展的次数
    int* p = NULL;
    vector<int> v1;
    v1.reserve(100000);         //注释这一行代码,看一下结果差异
    for (int i = 0; i < 100000; i++)
    {
        v1.push_back(i);
        if (p != &v1[0])
        {
            p = &v1[0];
            num++;
        }
    }
    cout << num << endl;

}

int main()
{
    test01();
    return 0;
}

如果数据量比较大,可以一开始就使用reverse预留足够的空间

三.deque容器

1.deque理解

双端数组,可以对头端和尾端进行插入和删除操作

dequevector区别:

  • vector对于头部的插入删除效率低,数据量越大,效率越低
  • deque相对而言,对头部的插入删除速度回比vector
  • vector访问元素时的速度会比deque快,这和两者内部实现有关

从C向C++15——常见容器1_赋值_02

deque工作原理:

  • deque内部有个中控器,维护每段缓冲区中的内容,缓冲区中存放真实数据
  • 中控器维护的是每个缓冲区的地址,使得使用deque时像一片连续的内存空间

从C向C++15——常见容器1_赋值_03

简单说一下deque在前后端操作的原理:中控器维护的是缓冲区的地址,如果在头端(尾端)插入一个元素,那么就在对应的第一个(最后一个)缓冲区插入该数据,若缓冲区满,那么就增加一个新的缓冲区添加进入中控器;如果在头端(尾端)删除一个元素,那么就在对应的缓冲区进行删除即可。同时,由于需要借助中控器进行数据操作,所以其操作速度没有vector那么快而已。

deque容器的迭代器也是支持随机访问的。

2.deque构造函数

deque<T> deqT;                            //默认构造形式
deque(beg,end);                           //构造函数将[beg,end)区间的元素拷贝给当前对象(左开右闭)
deque(n,elem);                            //将n个elem拷贝给当前对象
deque(const deque &deq);                  //拷贝构造函数

3.赋值操作

deque& operator=(const deque &deq);                   //重载=
assign(beg,end);                                     //将[beg,end)区间中的数据拷贝赋值给本身
assign(n,elem);                                       //将n个elem拷贝赋值给本身

dequevector的赋值操作类似。

4.deque大小操作

deque.empty();                         //判断容器是否为空
deque.size();                          //返回容器中元素的个数
deque.resize(int num);                 //重新指定容器的大小为num,若容器变长,则以默认值0填充新位置
                                       //如果容器变短,则末尾超出容器大小的元素被删除
deque.resize(int num,elem);            //重新指定容器的大小为num,若容器变长,则以元素elem填充新位置
                                       //如果容器变短,则末尾超出容器大小的元素被删除

dequevector一样,但是其没有容量的概念,因为它是可以无限扩充的。

5.插入和删除

两端插入或删除操作:

push_back(elem);              //在容器尾部添加一个数据
push_front(elem);                 //在容器头部插入一个数据
pop_back();                   //删除容器的最后一个数据
pop_front();                  //删除容器的第一个数据

可以通过deque的理解概念图得出来。

指定位置操作:

insert(pos,elem);              //在pos位置插入一个elem元素的拷贝,返回新数据的位置
insert(pos,n,elem);            //在pos位置下插入n个elem数据,无返回值
insert(pos,beg,end);           //在pos位置插入[beg,end)区间的数据,无返回值
clear();                       //情况容器的所有数据
erase(beg,end);                //删除[beg,end)区间的数据,无返回值
erase(pos);                    //删除pos位置的数据,返回下一个数据的位置

vector一样,在指定位置插入即pos都是迭代器(广义指针)。

6.deque数据存取

at(int dex);                 //返回索引idx所指的数据
operator[];                  //返回索引idx所指的数据
front();                     //返回容器中第一个数据元素
back();                      //返回容器中最后一个数据元素

7.deque排序

sort(beg,end);                                 //把区间内的元素进行排序

begend也是迭代器,这里的排序是从小到大排序,需要导入算法的头文件。

其实sort()是算法里面的内容,而不像上面其他是成员函数。

四.案例练习

1.案例描述

有5名选手:选手ABCDE,10个评委分别对每一名选手打分,去除最高分,去除评委中最低分,取平均分。

2.实现步骤

  • 1.创建五名选手,放到vector
  • 2.遍历vector容器,取出来每一个选手,执行for循环,可以把10个评分打分存到deque容器中
  • 3.sort算法对deque容器中分数排序,去除最高和最低分
  • 4.deque容器遍历一遍,累加总分
  • 5.获取平均分

3.实现代码

#include <iostream>
#include <vector>
#include <string>
#include <deque>
#include <algorithm>
using namespace std;

//选手类
class Person 
{
public:
    Person(string name, int score)
    {
        this->m_name = name;
        this->m_score = score;
    }
    string m_name;
    int m_score;
};


//创建选手
void creatper(vector<Person> &v)
{
    string namestr = "ABCDE";
    for (int i = 0; i < 5; i++)
    {
        string name = "选手";
        name += namestr[i];

        int score = 0;
        Person p(name, score);
        v.push_back(p);
    }
}

void setscore(vector<Person>& v)
{
    for (vector<Person>::iterator it = v.begin(); it != v.end(); it++)
    {
        //把评委分数放入deque中
        deque<int> d;
        for (int i = 0; i < 10; i++)
        {
            int score = rand() % 41 + 60;
            d.push_back(score);
        }
        cout << it->m_name << "的分数为:" << endl;
        for (deque<int>::iterator dit = d.begin(); dit != d.end(); dit++)
        {
            cout<<*dit<<" ";
        }
        cout << endl;

        //去除最大值和最小值
        sort(d.begin(), d.end());
        d.pop_back();
        d.pop_front();

        //取平均分
        int sum = 0;
        for (deque<int>::iterator dit = d.begin(); dit != d.end(); dit++)
        {
            sum += *dit;
        }
        int avg = sum / d.size();

        it->m_score = avg;
    }
}

void showscore(vector<Person>& v)
{
    for (vector<Person>::iterator it = v.begin(); it != v.end(); it++)
    {
        cout << it->m_name << "的最后成绩是:" << it->m_score << endl;
    }

}
int main()
{
    //1.创建5个选手
    vector<Person> v;
    creatper(v);

    //2.开始打分
    setscore(v);

    //3.显示分数
    showscore(v);
    return 0;
}