C++进阶内容(函数模板、类模板、STL、算法)

C++进阶内容(函数模板、类模板、STL、算法)

STL特点

STL具有高可重用性,高性能,高移植性,跨平台的优点。

  • 高可重用性:STL中几乎所有的代码都采用了模板类和模版函数的方式实现,这相比于传统的由函数和类组成的库来说提供了更好的代码重用机会。关于模板的知识,已经给大家介绍了。
  • 高性能:如map可以高效地从十万条记录里面查找出指定的记录,因为map是采用红黑树的变体实现的。(红黑树是平衡二叉树的一种)
  • 高移植性:如在项目A上用STL编写的模块,可以直接移植到项目B上。
  • 跨平台:如用windows的Visual Studio编写的代码可以在Mac OS的XCode上直接编译。

STL数据结构类型

数据结构描述实现头文件
向量(vector)连续存储的元素
列表(list)由节点组成的双向链表,每个结点包含着一个元素
双队列(deque)连续存储的指向不同元素的指针所组成的数组
集合(set)由节点组成的红黑树,每个节点都包含着一个元素,节点之间以某种作用于元素对的谓词排列,没有两个不同的元素能够拥有相同的次序
多重集合(multiset)允许存在两个次序相等的元素的集合
优先队列(priority_queue)元素的次序是由作用于所存储的值对上的某种谓词决定的的一种队列
映射(map)由{键,值}对组成的集合,以某种作用于键对上的谓词排列
多重映射(multimap)允许键对有相等的次序的映射

string相关操作

void main25()
{
    string s1 = "wbm hello wbm 111 wbm 222 wbm 333";
    size_t index = s1.find("wbm", 0);
    //从pos开始查找字符c在当前字符串的位置 
    cout << "index: " << index; 
     /*
     int rfind(char c, int pos=npos) const;   //从pos开始从后向前查找字符c在当前字符串中的位置 
     void swap(string &s2);    //交换当前字符串与s2的值
     string &insert(int pos, const string &s);
     //前两个函数在pos位置插入字符串s
     string &insert(int pos, int n, char c);  //在pos位置 插入n个字符
     string &erase(int pos=0, int n=npos);  //删除pos开始的n个字符,返回修改后的字符串
     */

    //求itcast出现的次数
    size_t offindex = s1.find("wbm", 0);
    while (offindex != string::npos)
    {
        cout << "在下标index: " << offindex << "找到wbm\n";
        offindex = offindex + 1;
        offindex = s1.find("wbm", offindex);
    }

    //替换 
    string s2 = "wbm hello wbm 111 wbm 222 wbm 333";
    s2.replace(0, 3, "wbm");
    cout << s2 << endl;

    //求itcast出现的次数
    offindex = s2.find("wbm", 0);
    while (offindex != string::npos)
    {
        cout << "在下标index: " << offindex << "找到wbm\n";
        s2.replace(offindex, 3, "WBM");
        //删除从pos开始的n个字符,然后在pos处插入串s
        offindex = offindex + 1;
        offindex = s1.find("wbm", offindex);
    }
    cout << "替换以后的s2:" << s2 << endl; 
}

void main27()
{
    string s2 = "AAAbbb";
    transform(s2.begin(), s2.end(), s2.begin(), toupper);
    cout << s2 << endl;

    string s3 = "AAAbbb";
    transform(s3.begin(), s3.end(), s3.begin(), tolower);
    cout << s3 << endl;
}

vector相关操作

vector<int> vecIntC(3,9); //此代码运行后,容器vecIntB就存放3个元素,每个元素的值是9。
int  iArray[] = {0,1,2,3,4};
vector<int>  vecIntA( iArray,  iArray+5 );
vector<int> vecIntB (  vecIntA.begin() , vecIntA.end()  );   //用构造函数初始化容器vecIntB 

//插入删除例子
vecInt是vector<int>  声明的容器,现已包含1,2,3元素。
int iSize = vecInt.size();      //iSize == 3;
bool bEmpty = vecInt.empty();   // bEmpty == false;
执行vecInt.resize(5);  //此时里面包含1,2,3,0,0元素。
再执行vecInt.resize(8,3);  //此时里面包含1,2,3,0,0,3,3,3元素。
再执行vecInt.resize(2);  //此时里面包含1,2元素。
vector<int> vecInt;
vecInt.push_back(1);  //在容器尾部加入一个元素
vecInt.pop_back();    //移除容器中最后一个元素
vecA.insert(vecA.begin(), 11);      //{11, 1, 3, 5, 7, 9}
vecA.insert(vecA.begin()+1,2,33);       //{11,33,33,1,3,5,7,9}
vecA.insert(vecA.begin() , vecB.begin() , vecB.end() ); //{2,4,6,8,11,33,33,1,3,5,7,9}
vector.clear(); //移除容器的所有数据
vector.erase(beg,end);  //删除[beg,end)区间的数据,返回下一个数据的位置。
vector.erase(pos);    //删除pos位置的数据,返回下一个数据的位置。



vector<int> vecInt;    //假设包含1 ,3 ,5 ,7 ,9
vecInt.at(2) == vecInt[2]   ;       //5
vecInt.at(2) = 8;  或  vecInt[2] = 8;
vecInt 就包含 1, 3, 8, 7, 9int iF = vector.front();    //iF==1
int iB = vector.back(); //iB==9
vector.front() = 11;    //vecInt包含{11,3,8,7,9}
vector.back() = 19; //vecInt包含{11,3,8,7,19}


//迭代器
vector<int>  vecInt; //假设包含1,3,5,7,9元素
vector<int>::iterator it;       //声明容器vector<int>的迭代器。
it = vecInt.begin();    // *it == 1
++it;               //或者it++;  *it == 3  ,前++的效率比后++的效率高,前++返回引用,后++返回值。
it += 2;        //*it == 7
it = it+1;      //*it == 9
++it;               // it == vecInt.end();  此时不能再执行*it,会出错!

//正向遍历:
for(vector<int>::iterator it=vecInt.begin(); it!=vecInt.end(); ++it)
{
      int iItem = *it; 
      cout << iItem;    //或直接使用  cout << *it;
}
//这样子便打印出1 3 5 7 9

//逆向遍历:
for(vector<int>::reverse_iterator rit=vecInt.rbegin(); rit!=vecInt.rend(); ++rit)    //注意,小括号内仍是++rit
{
        int iItem  = *rit;
      cout << iItem;    //或直接使用cout << *rit;
}
//此时将打印出9,7,5,3,1
//注意,这里迭代器的声明采用vector<int>::reverse_iterator,而非vector<int>::iterator。
//备注:以上两种分别是vector<int>::iterator 与vector<int>::reverse_iterator 的只读形式,使用这两种迭代器时,不会修改到容器中的值。容器中的insert和erase方法仅接受这四种类型中的iterator,其它三种不支持。

queue相关操作

  • deque是“double-ended queue”的缩写,和vector一样都是STL的容器,deque是双端数组,而vector是单端的。 deque在接口上和vector**非常相似**,在许多操作的地方可以直接替换。
//增加删除
deque.push_back(elem);  //在容器尾部添加一个数据
deque.push_front(elem); //在容器头部插入一个数据
deque.pop_back();           //删除容器最后一个数据
deque.pop_front();      //删除容器第一个数据

deque.at(idx);  //返回索引idx所指的数据,如果idx越界,抛出out_of_range。
deque[idx];  //返回索引idx所指的数据,如果idx越界,不抛出异常,直接出错。
deque.front();   //返回第一个数据。
deque.back();  //返回最后一个数据


//迭代器
/*
deque.begin();  //返回容器中第一个元素的迭代器。
deque.end();  //返回容器中最后一个元素之后的迭代器。
deque.rbegin();  //返回容器中倒数第一个元素的迭代器。
deque.rend();   //返回容器中倒数最后一个元素之后的迭代器。
*/
deque<int> deqInt;
        deqInt.push_back(1);
        deqInt.push_back(3);
        deqInt.push_back(5);
        deqInt.push_back(7);
        deqInt.push_back(9);

        for (deque<int>::iterator it=deqInt.begin(); it!=deqInt.end(); ++it)
        {
            cout << *it;
            cout << "";
        }
    // 1 3 5 7 9

        for (deque<int>::reverse_iterator rit=deqInt.rbegin(); rit!=deqInt.rend(); ++rit)
        {
            cout << *rit;
            cout << "";
        }
    //9 7 5 3 1
//赋值
deque.assign(beg,end);    //将[beg, end)区间中的数据拷贝赋值给本身。注意该区间是左闭右开的区间。
deque.assign(n,elem);  //将n个elem拷贝赋值给本身。
deque<int> deqIntB(deqIntA.begin(),deqIntA.end());      //1 3 5 7 9
deque<int> deqIntC(5,8);                            //8 8 8 8 8
deque<int> deqIntD(deqIntA);                        //1 3 5 7 9


//删除和vector一样
deque.clear();  //移除容器的所有数据
deque.erase(beg,end);  //删除[beg,end)区间的数据,返回下一个数据的位置。
deque.erase(pos);    //删除pos位置的数据,返回下一个数据的位置。

stack相关操作

  • stack是堆栈容器,是一种“先进后出”的容器。
  • stack是简单地装饰deque容器而成为另外的一种容器。
  • #include
stack.push(elem);   //往栈头添加元素
stack.pop();   //从栈头移除第一个元素
int iTop = stkIntA.top();       //9
stack.empty();   //判断堆栈是否为空
stack.size();        //返回堆栈的大小

queue相关操作

  • queue是队列容器,是一种“先进先出”的容器。
  • queue是简单地装饰deque容器而成为另外的一种容器。
  • #include
queue.push(elem);   //往队尾添加元素
queue.pop();   //从队头移除第一个元素
queue.back();   //返回最后一个元素
queue.front();   //返回第一个元素
queue.empty();   //判断队列是否为空
queue.size();        //返回队列的大小

list相关操作

list.push_back(elem);      //在容器尾部加入一个元素
list.pop_back();              //删除容器中最后一个元素
list.push_front(elem);     //在容器开头插入一个元素
list.pop_front();              //从容器开头移除第一个元素
list.front();   //返回第一个元素。
list.back();  //返回最后一个元素。
list.begin();                     //返回容器中第一个元素的迭代器。
list.end();                       //返回容器中最后一个元素之后的迭代器。
list.rbegin();         //返回容器中倒数第一个元素的迭代器。
list.rend();         //返回容器中倒数最后一个元素的后面的迭代器。

//插入
lstA.insert(lstA.begin(), 11);      //{11, 1, 3, 5, 7, 9}
lstA.insert(++lstA.begin(),2,33);       //{11,33,33,1,3,5,7,9}
lstA.insert(lstA.begin() , lstB.begin() , lstB.end() ); //{2,4,6,8,11,33,33,1,3,5,7,9}

//删除
list.clear();       //移除容器的所有数据
list.erase(beg,end);  //删除[beg,end)区间的数据,返回下一个数据的位置。
list.erase(pos);    //删除pos位置的数据,返回下一个数据的位置。
lst.remove(elem);   //删除容器中所有与elem值匹配的元素。

//删除区间内的元素
lstInt是用list<int>声明的容器,现已包含按顺序的1,3,5,6,9元素。
list<int>::iterator itBegin=lstInt.begin();
++ itBegin;
list<int>::iterator itEnd=lstInt.begin();
++ itEnd;
++ itEnd;
++ itEnd;
lstInt.erase(itBegin,itEnd);
//此时容器lstInt包含按顺序的1,6,9三个元素


//反序排列
lst.reverse();     //反转链表,比如lst包含1,3,5元素,运行此方法后,lst就包含5,3,1元素

set和multiset相关操作

  • set是一个集合容器,其中所包含的元素是唯一的,集合中的元素按一定的顺序排列元素插入过程是按排序规则插入,所以不能指定插入位置。
  • set采用红黑树变体的数据结构实现,红黑树属于平衡二叉树。在插入操作和删除操作上比vector快。
  • set不可以直接存取元素。(不可以使用at.(pos)与[]操作符)。
  • multiset与set的区别:set支持唯一键值,每个元素值只能出现一次;而multiset中同一值可以出现多次
  • 不可以直接修改set或multiset容器中的元素值,因为该类容器是自动排序的。如果希望修改一个元素值,必须先删除原有的元素,再插入新的元素。
  • #include  
set.insert(elem);     //在容器中插入元素。
set.begin();         //返回容器中第一个数据的迭代器。
set.end();          //返回容器中最后一个数据之后的迭代器。
set.rbegin();      //返回容器中倒数第一个元素的迭代器。
set.rend();       //返回容器中倒数最后一个元素的后面的迭代器。

set<int,less<int> >  setIntA;    //容器默认升序   该容器是按升序方式排列元素。
set<int,greater<int>> setIntB;   //该容器是按降序方式排列元素。


//下面举出greater<int>的简易实现原理。
struct greater
{
bool operator() (const int& iLeft, const int& iRight)
{
       return (iLeft>iRight);    //如果是实现less<int>的话,这边是写return (iLeft<iRight);
}
}
// 容器就是调用函数对象的operator()方法去比较两个值的大小。

//例子
//题目:学生包含学号,姓名属性,现要求任意插入几个学生对象到set容器中,使得容器中的学生按学号的升序排序。
//学生类
class CStudent
{
    public:
        CStudent(int iID, string strName)
        {
            m_iID = iID;
            m_strName = strName;
        }
     int m_iID;     //学号
     string m_strName;  //姓名
}
//为保持主题鲜明,本类不写拷贝构造函数,不类也不需要写拷贝构造函数。但大家仍要有考虑拷贝构造函数的习惯。

//函数对象
struct StuFunctor
{
        bool operator()  (const CStudent &stu1, const CStudent &stu2)
        {
            return (stu1.m_iID<stu2.m_iID);
        }
}
//main函数
void main()
{
        set<CStudent, StuFunctor> setStu;
        setStu.insert(CStudent(3,"小张"));
        setStu.insert(CStudent(1,"小李"));
        setStu.insert(CStudent(5,"小王"));
        setStu.insert(CStudent(2,"小刘"));
        //此时容器setStu包含了四个学生对象,分别是按姓名顺序的“小李”,“小刘”,“小张”,“小王” 
}

set.size();          //返回容器中元素的数目
set.empty();         //判断容器是否为空
set.clear();        //清除所有元素
set.erase(pos);      //删除pos迭代器所指的元素,返回下一个元素的迭代器。
set.erase(beg,end);  //删除区间[beg,end)的所有元素   ,返回下一个元素的迭代器。
set.erase(elem);     //删除容器中值为elem的元素。
set.find(elem);      //查找elem元素,返回指向elem元素的迭代器。
set.count(elem);     //返回容器中值为elem的元素个数。对set来说,要么是0,要么是1。对multiset来说,值可能大于1。
set.lower_bound(elem);  //返回第一个>=elem元素的迭代器。
set.upper_bound(elem);  //  返回第一个>elem元素的迭代器。
set.equal_range(elem);  //返回容器中与elem相等的上下限的两个迭代器。上限是闭区间,下限是开区间,如[beg,end)。

set和multiset相关操作

  • map是标准的关联式容器,一个map是一个键值对序列,即(key,value)对。它提供基于key的快速检索能力。
  • map中key值是唯一的。集合中的元素按一定的顺序排列。元素插入过程是按排序规则插入,所以不能指定插入位置。
  • map的具体实现采用红黑树变体的平衡二叉树的数据结构。在插入操作和删除操作上比vector快。
  • map可以直接存取key所对应的value,支持[]操作符,如map[key]=value。
  • multimap与map的区别:map支持唯一键值,每个键只能出现一次;而multimap中相同键可以出现多次。multimap不支持[]操作符。
  • #include
map<T1,T2> mapTT; 
multimap<T1,T2>  multimapTT;  
如:
map<int, char> mapA;
map<string,float> mapB;
//其中T1,T2还可以用各种指针类型或自定义类型

//插入
map.insert(...);    //往容器插入元素,返回pair<iterator,bool>
//在map中插入元素的三种方式:
//假设  map<int, string> mapStu;

//一、通过pair的方式插入对象
mapStu.insert(  pair<int,string>(3,"小张")  );
//二、通过pair的方式插入对象
mapStu.inset(make_pair(-1, “校长-1”));
//三、通过value_type的方式插入对象
mapStu.insert(  map<int,string>::value_type(1,"小李")  );
//四、通过数组的方式插入值
mapStu[3] = “小刘";
mapStu[5] = “小王"//插入的例子 
map<int, string> mapA;
pair< map<int,string>::iterator, bool > pairResult = mapA.insert(pair<int,string>(3,"小张")); 
//插入方式一
int iFirstFirst = (pairResult.first)->first;        //iFirst == 3;
string strFirstSecond = (pairResult.first)->second;     //strFirstSecond为"小张"
bool bSecond = pairResult.second;                           //bSecond == true;
//插入方式二 
mapA.insert(map<int,string>::value_type(1,"小李"));           

mapA[3] = "小刘";         //修改value
//插入方式三
mapA[5] = "小王";         
//执行插入 string() 操作,返回的str1的字符串内容为空。
string str1 = mapA[2];          
//迭代器遍历
    for (map<int,string>::iterator it=mapA.begin(); it!=mapA.end(); ++it)
    {
        pair<int, string> pr = *it;
        int iKey = pr.first;
        string strValue = pr.second;
    }

map<T1,T2,less<T1> >  mapA;  //该容器是按键的升序方式排列元素。未指定函数对象,默认采用less<T1>函数对象。
map<T1,T2,greater<T1>> mapB;   //该容器是按键的降序方式排列元素。
//less<T1>与greater<T1>  可以替换成其它的函数对象functor。
//可编写自定义函数对象以进行自定义类型的比较,使用方法与set构造时所用的函数对象一样。
map.begin();  //返回容器中第一个数据的迭代器。
map.end();  //返回容器中最后一个数据之后的迭代器。
map.rbegin();  //返回容器中倒数第一个元素的迭代器。
map.rend();   //返回容器中倒数最后一个元素的后面的迭代器。


//插入  赋值   交换
        map<int, string> mapA;
        mapA.insert(pair<int,string>(3,"小张"));  
        mapA.insert(pair<int,string>(1,"小杨"));  
        mapA.insert(pair<int,string>(7,"小赵"));  
        mapA.insert(pair<int,string>(5,"小王"));  

        map<int ,string> mapB(mapA);                   //拷贝构造

        map<int, string> mapC;
        mapC = mapA;                                //赋值

        mapC[3] = "老张";
         mapC.swap(mapA);           //交换



        //删除区间内的元素
        map<int,string>::iterator itBegin=mapA.begin();
        ++ itBegin;
        ++ itBegin;
        map<int,string>::iterator itEnd=mapA.end();
        //此时容器mapA包含按顺序的{1,"小杨"}{3,"小张"}两个元素。
        mapA.erase(itBegin,itEnd);          
        mapA.insert(pair<int,string>(7,"小赵"));  
        mapA.insert(pair<int,string>(5,"小王"));  

        //删除容器中第一个元素
        //此时容器mapA包含了按顺序的{3,"小张"}{5,"小王"}{7,"小赵"}三个元素
        mapA.erase(mapA.begin());       
        //删除容器中key为5的元素
        mapA.erase(5);    
        //删除mapA的所有元素
        //容器为空
        mapA.clear();

        //map查找
        map.find(key);   查找键key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回map.end();
        map.count(keyElem);   //返回容器中key为keyElem的对组个数。对map来说,要么是0,要么是1。对multimap来说,值可能大于1。
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值