C++【14】(容器)

195 阅读4分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

一、常用容器

1.1 string容器

1.1.1 相关概念

C风格字符串(以空字符结尾的字符数组)太过复杂难于掌握,不适合大程序的开发,所以C++标准库定义了一种string类,定义在头文件<string>

Stringc风格字符串对比:Char*是一个指针,String是一个类

string封装了char,管理这个字符串,是一个char型的容器。 String封装了很多实用的成员方法查找find,拷贝copy,删除delete 替换replace,插入insert,不用考虑内存释放和越界,string实例化的对象释放空间时,会自动调用析构函数,所以在析构函数中释放空间了,所以不需要我们手动释放

string管理char*所分配的内存。每一次string的复制,取值都由string类负责维护,不用担心复制越界和取值越界等。

string开辟空间问题

如果string类实例化的对象前期赋值不超过15个字节,默认最大存放的字符串的长度为15个字节,所以如果赋值超过15个字节,==会释放原来的空间,开辟新的空间==,所以不要事先保存string的地址,因为有可能操作的地址已经不再是当前对象的了,所以最好实时保存

cout << "容量:" << s1.capacity() << endl;

1.1.2 string容器常用操作

1.1.2.1 string构造函数

string();//创建一个空的字符串 例如: string str;
string(const string& str);//使用一个string对象初始化另一个string对象
string(const char* s);//使用字符串s初始化
string(int n, char c);//使用n个字符c初始化
void test1()
{
    //创建一个string容器
    string s1("hello world");
    cout << s1 << endl;

    string s2 = s1;
    cout << s2 << endl;

    string s3(10, 'w');
    cout << s3 << endl;
}

1.1.2.2 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个字符赋值给字符串
void test2()
{
    string s1;
    s1 = "nihao beijing";
    cout << s1 << endl;

    string s2;
    s2 = s1;
    cout << s2 << endl;

    string s3;
    s3 = 'p';
    cout << s3 << endl;

    string s4;
    s4.assign("nihao beijing", 5);
    cout << s4 << endl;

    s4.assign(s1, 3, 8);
    cout << s4 << endl;
}

1.1.2.3 string存取字符操作


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

string拼接操作
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
void test3()
{
    string s1("hello world");
    //[]运算符重载函数和at函数都是用于获取一个字符,
    //但是at函数内部有异常处理机制,越界操作会抛出异常,
    //而[]运算符重载函数则不会
    //cout << s1[100] << endl;
    try {
        cout << s1.at(20) << endl;
    } catch (exception &e) {
        cout << e.what() << endl;
    }

    s1 = s1 + "nihao beijing";
    cout << s1 << endl;
    s1 += "hahaha";
    cout << s1 << endl;

    string s2;
    s2.append(s1, 0, 11);
    cout << s2 << endl;
}

1.1.2.4 string查找和替换


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.1.2.5 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个字符组成的字符串

1.1.2.6 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个字符

string和c-style字符串转换
//string 转 char*
string str = "itcast";
const char* cstr = str.c_str();
//char* 转 string
char* s = "itcast";
string str(s);

2.2 vector容器

3.2.1 相关概念

vector容器的操作类似数组,本质就是一个数组的操作

图片.png

3.2.2 vector容器的未雨绸缪机制

//vector容器的未雨绸缪机制
//vector当空间容量不够用时,会开辟新的空间,将原本空间的内容拷贝到
//新的空间,然后将原来的空间释放,如果每次都这样做,效率比较低,所以
//vector开辟新空间时,会多预留出来一部分,这样做就不会多次开辟空间了
#include <iostream>
#include <vector>

using namespace std;

void test1()
{
    vector<int> v;

    for(int i = 0; i <= 30; i++)
    {
        v.push_back(i);
        cout << "容量:" << v.capacity() << ",实际:" << v.size() << endl;
    }
}

int main()
{
    test1();

    return 0;
}

执行结果

图片.png

2.2.3 vector常用API操作

2.2.3.1 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));

2.2.3.2 vector常用赋值操作

assign(beg, end);//将[beg, end)区间中的数据拷贝赋值给本身。
assign(n, elem);//将n个elem拷贝赋值给本身。
vector& operator=(const vector  &vec);//重载等号操作符
swap(vec);// 将vec与本身的元素互换。
#include <iostream>
#include <vector>

using namespace std;

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

void test1()
{
    int arr[] = {100, 200, 300, 400, 500, 600};
    vector<int> v(arr, arr+4);
    vector<int> v1;

    Printfvector(v);

    v1.assign(v.begin(), v.end());
    Printfvector(v1);
    v1.assign(10, 666);
    Printfvector(v1);

    cout << "v: 容量=" << v.capacity() << ",实际=" << v.size() << endl;
    cout << "v1: 容量=" << v1.capacity() << ",实际=" << v1.size() << endl;

    //调用swap函数交换两个容器之后,同两个容器的容量和实际使用都会发生变化
    v1.swap(v);
    cout << "*********************" << endl;
    cout << "v:";
    Printfvector(v);
    cout << "v1:";
    Printfvector(v1);

    cout << "v: 容量=" << v.capacity() << ",实际=" << v.size() << endl;
    cout << "v1: 容量=" << v1.capacity() << ",实际=" << v1.size() << endl;
    cout << "*********************" << endl;
}

int main()
{
    test1();

    return 0;
}

图片.png

2.2.3.3 vector大小操作

size();//返回容器中元素的个数
empty();//判断容器是否为空
resize(int num);//重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
resize(int num, elem);//重新指定容器的长度为num,若容器变长,则以elem值填充新位置。如果容器变短,则末尾超出容器长>度的元素被删除。
capacity();//容器的容量
reserve(int len);//容器预留len个元素长度,预留位置不初始化,元素不可访问。

vector数据存取操作
at(int idx); //返回索引idx所指的数据,如果idx越界,抛出out_of_range异常。
operator[];//返回索引idx所指的数据,越界时,运行直接报错c
front();//返回容器中第一个数据元素
back();//返回容器中最后一个数据元素
#include <iostream>
#include <vector>

using namespace std;

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

void test1()
{
    int arr[] = {100, 200, 300, 400, 500, 600};
    vector<int> v(arr, arr+6);

    cout << "第一个元素:" << v.front() << endl;
    cout << "最后一个元素:" << v.back() << endl;

    cout << v[0] << endl;
    //at函数内部有异常处理机制
    cout << v.at(3) << endl;

    cout << "*************" << endl;
    Printfvector(v);
    cout << "v: 容量=" << v.capacity() << ",实际=" << v.size() << endl;
    //resize的使用
    //如果设置的值大于原本的容量,则容量和实际使用都会改变,并且将多与实际使用的位置填0
    //如果设置的值小于原本的容量,则容量不变,实际使用的变为设置的值,其他位置的值会移除
    //v.resize(10);
    //v.resize(4);

    //reserve的使用
    //如果设置的值大于原本的容量,则容量改变,但是实际使用不变
    //如果设置的值小于原本的容量,则容量和实际使用都不变
    //v.reserve(10);
    v.reserve(4);
    Printfvector(v);
    cout << "v: 容量=" << v.capacity() << ",实际=" << v.size() << endl;
}

int main()
{
    test1();

    return 0;
}

2.2.3.4 vector插入和删除操作

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

for_each

for_each内部实现:
        template<class InputIterator, class Function>
          Function for_each(InputIterator first, InputIterator last, Function fn)
        {
          while (first!=last) {
            fn (*first);
            ++first;
          }
          return fn;      // or, since C++11: return move(fn);
        }
template <class InputIterator, class Function>
Function for_each (InputIterator first, InputIterator last, Function fn);
功能:获取容器中的元素
参数:
    first:起始迭代器
    last:结束迭代器
    fn:回调函数,for_each函数会将每一个元素传递给回调函数,没传一次,回调函数调用一次

for_each(v.begin(), v.end(), printVectorInt);
void printVectorPerson(Person obj)
{
    cout << obj.name << ", " << obj.score << endl;
}

sort

//排序算法
//sort是一个全局函数
//默认按照从小到大的顺序排列
//sort(v.begin(), v.end());
//也可以自己设置策略来排序
//sort(v.begin(), v.end(), greater<int>());
class Person{
public:
    Person(){}
    Person(string name, int score):name(name),score(score){}
    string name;
    int score;
};

void printVectorPerson(Person obj)
{
    cout << obj.name << ", " << obj.score << endl;
}

//编写策略
bool myCompare(Person obj1, Person obj2)
{
    return obj1.score < obj2.score;
}

void test6()
{
    vector<Person> v;
    v.push_back(Person("zhangsan", 90));
    v.push_back(Person("lisi", 80));
    v.push_back(Person("wangwu", 100));

    for_each(v.begin(), v.end(), printVectorPerson);

    //sort函数默认只能排序数值型数据,如果要排序类的对象,需要自己编写策略
    sort(v.begin(), v.end(), myCompare);

    for_each(v.begin(), v.end(), printVectorPerson);
}

int main()
{
    test6();

    return 0;
}

截取字符串

void test()
{
    string s1("123:456:789:666");
    string s2;
    int n = 0, pos = 0;

    while(1)
    {
        if(n == -1)break;
        n = s1.find(':',pos);

        //cout << pos << endl;
        s2 = s1.substr(pos,n-pos);
        cout << s2 << endl;

        pos = n + 1;
    }
}

图片.png

作业:

学习deque容器

​ 有5名选手:选手ABCDE,10个评委分别对每一名选手打分,去除最高分,去除最低分,取平均分,最终将前三名选手的姓名和分数输出 rand函数,srand设置随机种子 rand()%41 + 60; 排序sort,然后删除头尾

#include <iostream>
#include <vector>
#include <algorithm>
#include <deque>
#include <time.h>

using namespace std;

//有5名选手:选手ABCDE,10个评委分别对每一名选手打分,去除最高分,
//去除最低分,取平均分,最终将前三名选手的姓名和分数输出

class Person{
public:
    Person(){}
    Person(string name, double score):name(name),score(score){}
    void pringMsg()
    {
        cout << "姓名:" << this->name << ",平均分:" << this->score << endl;
    }
    void setScore(double score)
    {
        this->score = score;
    }
    string getName()
    {
        return this->name;
    }

    double getScore()
    {
        return this->score;
    }

private:
    string name;
    double score;
};

void printDeque(double val)
{
    cout << val << " ";
}

void getScore(Person &obj)
{
    cout << "选手" << obj.getName() << "的分数为:" << endl;
    //创建一个deque容器保存每一位选手的分数
    deque<double> myscore;
    for(int i = 0; i < 10; i++)
    {
        myscore.push_back(rand() % 41 + 60);
    }

    //排序
    sort(myscore.begin(), myscore.end());

    for_each(myscore.begin(), myscore.end(), printDeque);
    cout << endl;

    //删除最大和最小的成绩
    cout << "删除最高分和最低分:" << endl;
    myscore.pop_back();
    myscore.pop_front();

    for_each(myscore.begin(), myscore.end(), printDeque);
    cout << endl;

    //求平均值
    double sum = 0;
    for(deque<double>::iterator it = myscore.begin(); it < myscore.end(); it++)
    {
        sum += *it;
    }

    cout << "平均值为:" << sum / 8.0 << endl;
    obj.setScore(sum / 8.0);
}

void printVector(Person obj)
{
    obj.pringMsg();
}

bool myCompare(Person obj1, Person obj2)
{
    return obj1.getScore() > obj2.getScore();
}

int main()
{
    //设置随机种子,保证每次执行获取到的随机值不一样
    srand(time(NULL));

    //创建保存选手姓名和平均分的容器
    vector<Person> v;
    v.push_back(Person("张三", 0));
    v.push_back(Person("李四", 0));
    v.push_back(Person("王五", 0));
    v.push_back(Person("赵六", 0));
    v.push_back(Person("田七", 0));

    vector<Person>::iterator it = v.begin();
    for(int i = 0; i < 5; i++)
    {
        getScore(*it);
        it++;
    }

    //获取完分数之后进行排序
    sort(v.begin(), v.end(), myCompare);

    cout << "*******************" << endl;
    cout << "最终的选手成绩为:" << endl;
    for_each(v.begin(), v.end(), printVector);
    
    cout << "冠军为:";
    (*(v.begin())).pringMsg();
    cout << "亚军为:";
    (*(v.begin() + 1)).pringMsg();
    cout << "季军为:";
    (*(v.begin() + 2)).pringMsg();

    return 0;
}
#include <iostream>
#include <exception>
#include <vector>
#include <algorithm>
#include <numeric>
#include <stdlib.h>
#include <time.h>
#include <deque>
#include <iterator>


using namespace std;

class Person{
public:
    Person(){}
    Person(string name, int score):name(name),score(score){}

    string Getname()
    {
        return this->name;
    }

    int Getscore()
    {
        return this->score;
    }

    void Setname(string name)
    {
        this->name = name;
    }

    void Setscore(int score)
    {
        this->score = score;
    }

private:
    string name;
    int score;
};

void printVectorPerson(Person obj)
{
    cout << obj.Getname() << ", " << obj.Getscore() << endl;
}

//编写策略
bool myCompare(Person obj1, Person obj2)
{
    return obj1.Getscore() > obj2.Getscore();
}



Person Average_scores(Person &p)
{

    deque<int> d;
    deque<int>::iterator it;
    int add = 0;

    for(int i=0; i<10; i++)
    {
        d.push_back(int(rand()%41 + 60));
    }
    sort(d.begin(), d.end());
    d.pop_back();
    d.pop_front();

    for(it = d.begin(); it < d.end(); it++)
    {
        add += (*it) ;

    }

    add=add/8;
    p.Setscore(add);

    cout << p.Getname() << "的平均成绩为:" << add << endl;

    return p;
}

void test()
{
    srand((unsigned)time(NULL));
    Person A, B, C, D, E;
    vector<Person> v;

    A.Setname("A");
    B.Setname("B");
    C.Setname("C");
    D.Setname("D");
    E.Setname("E");

    A = Average_scores(A);
    B = Average_scores(B);
    C = Average_scores(C);
    D = Average_scores(D);
    E = Average_scores(E);

    v.push_back(Person(A.Getname(),A.Getscore()));
    v.push_back(Person(B.Getname(),B.Getscore()));
    v.push_back(Person(C.Getname(),C.Getscore()));
    v.push_back(Person(D.Getname(),D.Getscore()));
    v.push_back(Person(E.Getname(),E.Getscore()));

    sort(v.begin(), v.end(),myCompare);

    cout << endl << "前三名为:" << endl;
    for_each(v.begin(), v.begin()+3, printVectorPerson);
}


int main()
{
    test();

    return 0;
}