STL容器学习第一篇(记录

1 顺序容器


顺序容器有3种 : Vector list 和 deque
容器元素的初始化:方式有5种。来验证一下:

#include <iostream>
#include <deque>
#include <list>
#include <set>
#include <map>
#include <queue>
#include <cstdlib>
#include <ctime>

using namespace std;

void print(vector< int > & s)
{
vector< int >::iterator i = s.begin();
for(; i < s.end(); i ++)
cout << *i ;
cout << endl;
}
int main(void)
{
//共有5种方式初始化一个容器
vector< int > ivec; // 1)创建空容器(适用于所有容器)
vector< int > ivec1(ivec); // 2)用一个容器初始化另外一个容器(必须具有相同的容器类型,并存放相同类型的元素)
ivec.push_back(3);
ivec.push_back(5);
vector< int > ivec2(ivec.begin(),ivec.end()); //适用所有容器,并且容器内部类型要兼容,容器类型可以不相同。内容为两个迭代器之间


//list< int > s;
//vector< int > ivec5(s.begin(),s.end());//正确 ,只需要 内部元素类型相同,就可以使用这种构造方式
//list< int > s(ivec); //错误
//vector< double > s(ivec); //错误。

//下面这两个的方式只适用于顺序容器 vector deque list
vector< int > ivec3(5,3); //用n个t值元素 初始化容器。
vector< int > ivec4(5); //创建有n个值的容器。
print(ivec);
print(ivec1);
print(ivec2);
print(ivec3);
print(ivec4);
}



打印输出看下结果:

OK,继续往下看。
用迭代器来初始化 一个 容器:
容器类型可以不相同。容器内的元素类型也可以不相同。只要 两者相互兼容就可以。采用这种初始化形式有两种好处,一是可以 复制不能直接复制的容器,另外一种可以复制其他容器的一个副本。(一部分)

)
vector< int > ivec; // 1)创建空容器vector< int > ivec1(ivec); // 2)用一个容器初始化另外一个容器ivec.push_back(3);ivec.push_back(5);ivec.push_back(7);ivec.push_back(9);list< double >list1( ivec.begin(), ivec.end() );vector< int >::iterator mid = ivec.begin() + ivec.size() / 2;list< double >listBegin( ivec.begin(),mid );list< double >listEnd( mid, ivec.end());print(list1);print(listBegin);print(listEnd);vector< int > ivec2(ivec.begin(),ivec.end());//迭代器就是指针,这样可以用指针代替迭代器就行初始化,看下面char * words[] = { "tao", "tao", "is", "a sb"};size_t words_size = sizeof(words)/sizeof( char *);list< string > slist(words,words + words_size);list< string >::iterator i = slist.begin();while(i != slist.end()){cout << *i << endl ;i++;}cout << endl;




这里有一个 需要尤其注意的地方,用迭代器来初始化一个 容器的时候,第2个指针是指向最后一个元素下一个位置的,也就是第2个指针指向的值是不复制到新的容器的。
直接分配 和初始化指定数目的元素:例如; vector< int > s(5) 只提供个数, vector< int > s(5,3) 提供个数而且提供初始值
当你不提供初始化表达式的时候,采用这种方式的时候,类型必须是内置或者复合类型,或者是提供了默认构造函数的类类型,如果没有默认构造函数,则必须显示提供初始化表达式。这个时候我们可以回想起来为什么 如果你定义了一个带参数的构造函数,最好必须提供一个默认构造函数的原因。例子:
^_^

class X
{
public:
X(const int & data ,const string &s):data(data),s(s){}
~X(){}
private:
int data;
string s;
};
vector< X > s(5);



这个时候就出现错误信息:error: no matching function for call to ‘X::X()’| 没有匹配的函数:调用默认构造函数。
接受容器大小 做为形参的构造函数只适用与顺序容器,关联容器不可一这样初始化。


复制容器对象的构造函数和 使用两个迭代器的构造函数的区别:
复制容器对象构造函数只能复制 一个容器,必须要求2个容器的类型和内部元素的类型是一样的。
而使用两个迭代器构造函数的,不需要容器类型一样,而且也不需要容器内部元素类型一样,只要两种内部元素类型相互兼容(譬如 int 和double)


容器元素类型必须的两个约束:
1.支持赋值运算 2.元素对象必须可以复制。(引用就不支持一般意义的赋值运算,没有是引用类型的容器)




2.迭代器:和指针差不多的概念
每种容器都提供和自身容器类型相兼容的迭代器类型,看下面的例子:
    const vector< int > x;
    //vector< int >::iterator iter3 = s.begin();
    vector< int >::const_iterator iter4 = x.begin();
我们首先定义了一个const的容器,第二种定义方式是不对的。不能把const类型的iterator的赋值给non-const
就是说把const类型iterator赋值给non-const的是不允许的,原因不需要说了吧。解决办法有两个:
1就是把const去掉
2vector< int >::iterator i 改为vector< int >::const_iterator i


在C++中的容器类型中,只有vector和deque容器提供  迭代器 算数运算。以及使用== 和!=之外的关系操作符。

算数运算包括: iter + n   iter - n   iter += iter 2     iter -= iter 2   关系操作符: <   >    <=    >=
所有的迭代器 都支持 下面的操作:
iter ++ ,iter-- ,* iter,iter1 != iter2 ,iter1== iter2  

原因 :只有这两种容器可以提供快速随机的访问。可们可以根据位置直接访问到指定的容器元素。所以迭代器可以实现算数和关系运算。这个时候上面的例子中  list 迭代器的 比较采用的是!= 符号



迭代器范围:

first和end 迭代器 必须满足两个条件,才能形成迭代器范围:
1.指向同一个容器的元素或者超出末端的下一个位置。
2.两个迭代器如果不相等。则对first自增必须能达到end。  end绝对不能在first之前。
编译器是无法知道迭代器 指向的哪个容器,所以要程序员自己来保证这些 原则,如果不能保证,则会运行出错。


某些操作可以使迭代器失去效果:
在使用迭代器操作的时候,也特别留意哪些操作使得迭代器失效,使用无效迭代器将会导致严重的运行错误。,因此很多建议都是让我们获取insert或者erase返回的迭代器,以便用重新获取新的有效的迭代器进行正确的操作。
iter=vec.insert(iter);   
iter=vec.erase(iter);      
以vector为例,当我们插入一个元素时它的预分配空间不够时,它会重新申请一段新空间,将原空间上的元素复制到新的空间上去,然后再把新加入的元素放到新空间的尾部,以满足vector元素要求连续存储的目的。而后原空间会被系统撤销或征做他用,于是指向原空间的迭代器就成了类似于“悬垂指针”一样的东西,指向了一片非法区域。如果使用了这样的迭代器会导致严重的运行时错误就变得很自然了。这也是许多书上叙述vector在insert操作后“可能导致所有迭代器实效”的原因。而删除一个元素后,可能会把指向一个元素的迭代器指向另外一个元素。
则 可以使迭代器失去效果的操作可以分为以下两类:
  1.由于容器元素整体“迁移”导致存放原容器元素的空间不再有效,从而使得指向原空间的所有的迭代器失效。\
  2.由于删除元素使得某些元素次序发生变化使得原本指向某元素的迭代器不再指向希望指向的元素。
看第二种情况的一个例子:
 vector<int> vec;
    for(int i=0;i<10;i++)
        vec.push_back(i);

    vector<int>::iterator iter =vec.begin()+2;
    vector< int >::iterator iterEnd = vec.end();
    vec.erase(iter);//注:这里真的不建议这么写
    cout << *iterEnd << endl;
我们本意是输出 最后一个元素
输出结果为  0,因为 删除一个元素之后,使得指向最后一个元素的迭代器不再指向最后一个元素。(失效)
编写循环将元素插入到容易vector 或者 deque容器中,程序必须确保迭代器在每次循环后都能得到更新。//

容器内部类型别名:

size_type  (定义容器长度)
iterator
const_iterator
reverse_iterator   按逆序寻址元素的迭代器  ++操作是指向容器的前一个元素
const_reverse_iterator
difference_type(储存两个迭代器差值 的有符号整型,可为负数)
value_type  元素类型
reference
const_reference



begin 和end  操作产生容器的第一个元素的位置 和最后一个元素的下一个位置,rbegin和rend  产生逆序迭代器,指向最后一个元素和第一个元素前面的位置。
如果容器是const  类型的,则声明迭代器的时候一定要声明为const的。要不然不通过编译。


操作:

push_back()      3个顺序容器 全部使用              //返回为void类型
push_front()       只适用与list 和 deque,不能用与Vector(因为 vector需要提前分配连续的空间,所以不能在其前面插入元素)?  //返回为void类型
insert()
来写下 代码 测试一下:


void print(vector< int > & s)
{
    vector< int >::iterator i = s.begin();
    for(; i < s.end(); i ++)
        cout << *i ;
    cout << endl;
}
void print(list< int > & s)
{
    list< int  >::iterator i = s.begin();
    for(; i != s.end(); i ++) //不能用  《《 符号
        cout << *i ;
    cout << endl;
}
void print(deque< int > & s)
{
    deque< int  >::iterator i = s.begin();
    for(; i != s.end(); i ++) //不能用  《《 符号
        cout << *i ;
    cout << endl;
}
int main(void)
{
    vector< int > ivec;
    list< int > ilist;
    deque< int > ideque;
    for(int i = 0; i < 5; i++)
    {
        ivec.push_back(i);
        ilist.push_back(i);
        ideque.push_back(i);
    }
    for(int i = 0; i < 5; i++)
    {
        //ivec.push_front(i);error: ‘class std::vector<int>’ has no member named ‘push_front’|
        ilist.push_front(i);
        ideque.push_front(i);
    }
    //测试一下insert操作,有3种不同的插入方式
    //insert(p,t) 在迭代器p之前插入t元素,返回指向新元素的迭代器
    //insert(p,n,t)//在迭代器p之前插入n个t元素,返回void
    //insert(p,b,e)//在迭代器p之前插入迭代器b和e之前的元素,返回void

    int a[5]= {5, 6, 7, 8, 9};
    vector< int >::iterator p = ivec.begin();
    ivec.insert(p ,3);
    print(ivec);
    cout << *p << endl;
    ivec.insert(p,3,4);
    print(ivec);
    //ivec.insert(ivec.begin(),a + 2, a + 3);
    ivec.insert(ivec.begin(), &a[2], &a[3]);
    cout << *p << endl;

    list< int >::iterator pList = ilist.begin();
    ilist.insert(pList ,3);
    print(ilist);
    cout << *pList << endl;
    ilist.insert(pList,3,4);
    print(ilist);
    cout << *pList<< endl;

    deque< int >::iterator pDeque = ideque.begin();
    ideque.insert(pDeque,3);
    print(ideque);
    cout << *pDeque << endl;
    ideque.insert(pDeque,3,4);
    print(ideque);
    cout << *pDeque<< endl;
}


可以看到,insert 对 3个顺序容器都适用。在容器中添加元素的时候,系统是将元素值复制到容器里,新容器是存放原始元素的副本。

为什么insert 是在迭代器参数的前面插入一个元素呢:因为 迭代器是有可能在容器最后元素的 下一个位置,就是 end()返回的迭代器,这是位置上不存在元素,如果要在它后面插入的话是不对的,所以在迭代器参数前面插入元素。




避免存储  end 返回操作的 迭代器。添加或者删除deque或vector容器内的元素都会导致存储的迭代器失效。最后每次插入运算之后重新计算end迭代器的值。


访问元素:


back()   //返回容器最后一个元素的引用     c.back()  = *--c.end();
front()   //返回容器第一个元素的引用

//下面的两个 只适用于  Vector 和 deque  容器   
[n] 
.at(n)

使用越界的下标,或者调用空容器的front或者back函数,将会导致程序出现严重的错误/

int main()
{
    vector< int > ivec;
    for( int i = 0; i < 10; i++ )
        ivec.push_back(i);
    cout << ivec[0] << " " << ivec.at(0) << endl;
    cout << ivec.front() << endl;
    cout << ivec.back() << endl;


    vector< int > ivec2;
    //cout << ivec2[0] << " " << ivec2.at(0) << endl;  //terminate called after throwing an instance of 'std::out_of_range'
    //cout << ivec2.front() << endl;
    //cout << ivec2.back() << endl;   //Segmentation fault 
}



删除元素


erase(p)//删除迭代器p指向的元素,返回一个迭代器,指向删除元素位置之后的下一个元素,如果p本身为超出末端的下一个位置的迭代器,则函数无定义
erase(b,e)//删除 迭代器 b和e迭代器的前一个元素之间的元素,返回下一个元素的指针,如果e为超出末端的下一个位置的迭代器,则返回本身,
pop_back()  //返回void
pop_front()  //返回void   只适用于list  和 deque容器
clear()
例子:
int main()
{
    vector< int > ivec;
    for( int i = 0; i < 10; i++ )
        ivec.push_back(i);
    print(ivec);
    ivec.pop_back();
    print(ivec);
    vector< int >::iterator s1 = ivec.begin() + 2;
    vector< int >::iterator s2 = ivec.begin() + 3;
     s1 = ivec.erase(s1,s2);
    cout << *s1 << endl;
    print(ivec);
    //ivec.pop_front();//error: ‘class std::vector<int>’ has no member named ‘pop_front’| //pop_front()只适用与list和deque
}
结果:


pop_front()和pop_back()的返回类型 都是void,而不是 删除的元素值,要获取 删除的元素值,则必须在删除元素之前调用front函数来进行处理

earse()  和 pop_front    和pop_back()  函数使得指向删除元素的所有迭代器都会失效,对于 vector来说,指向删除节点后面的迭代器一般也失效了,对于deque,如果删除的时候不包含第一个元素或者最后一个元素,那么该deque容器相关的迭代器操作都会失效。

赋值与swap操作

=  赋值操作
赋值操作是作用于整个容器的。容器的类型必须相同 (数据类型也必须相同)

assign()操作首先删除掉左操作数的 所有元素,在重新复制,要求 容器类型 不是必须相同,元素类型也是只要相互兼容就可以。
assign(b,e) //将迭代器 b和e之间的元素复制到c中,b和e必须不是指向 调用assign操作的容器的迭代器。
assign(n,t)//重新设置 容器为 n个t值的 容器。

赋值操作和 assign操作将 左操作数容器的所有迭代器 失效。

swap()//swap操作使左容器的 所有迭代器 不失效。要求容器类型和元素类型 必须完全匹配才可以使用swap操作

看例子:、
int main()
{
    vector< int > ivec1;
    for( int i = 0; i < 10; i++ )
        ivec1.push_back(i);
    vector< int > ivec2;
    for( int i = 0; i < 10; i++, i++ )
        ivec2.push_back(i);
    print(ivec1);
    print( ivec2 );
    ivec1 = ivec2;
    cout << "ivec1 = ";
    print( ivec1 );
    vector< int > ivec3;
    ivec3.assign( ivec2.begin(), ivec2.end() );
    cout << "ivec3 = ";
    print( ivec3 );

    vector< int >::iterator sBegin = ivec3.begin();
    vector< int >::iterator sEnd = ivec3.end();
    ivec3.assign(3,5);
    cout << "ivec3 = ";
    print( ivec3 );
    // 根据打印可以看出来 迭代器失效了
    cout << "sBegin = " << *sBegin << endl;
    cout << "sEnd = " << *sEnd << endl;
    cout << "ivec3 = ";
    print( ivec3 );
    vector< int >::iterator sBegin1 = ivec3.begin();
    vector< int >::iterator sEnd1 = ivec3.end();
    cout << "sBegin1 = " << *sBegin1 << endl;
    cout << "sEnd1 = " << *--sEnd1 << endl;
    ivec3.swap(ivec1);
    cout << "swap ivec3 = ";
    print( ivec3 );
    cout << "sBegin1 = " << *sBegin1 << endl;
    cout << "sEnd1 = " << *sEnd1 << endl;
}

打印一下结果:

可以看到swap操作之后,原先的迭代器还是可以使用的。且仍旧指向原先的元素位置。(只不过是指向了 交换后的容器的相同的位置)


Vector容器的增长方式

Vector容器 的元素是以连续的方式存放的,每一个元素都紧挨着前一个元素存储。Vector容器在初始分配的时候,实际分配的容量总是比当前需要的空间多一些。
capacity()函数和reserver()成员函数,来处理内存分配细节。(特属于Vector的接口函数,list和 deque没有这种接口)
    list< int > ivecList;
    deque< int > ivecDeque;
    //cout << ivecList.capacity() << endl;   这样用是错误的。
    //cout << ivecDeque.capacity() << endl;


size()函数是指当前容器有多少个元素,而capacity函数是指的容器需要分配更多存储空间之前能够存储的元素总的个数。reserver操作告诉Vector应该预留多少元素空间

例子:
int main()
{
    vector< int > ivec1;
    cout << ivec1.size() << endl;
    cout << ivec1.capacity() << endl;
    for( int i = 0; i < 10; i++ )
        ivec1.push_back(i);

    cout << ivec1.size() << endl;
    cout << ivec1.capacity() << endl;
    ivec1.reserve(50);
    cout << ivec1.size() << endl;
    cout << ivec1.capacity() << endl;
    while( ivec1.size() != ivec1.capacity() )
        ivec1.push_back(0);
    cout << ivec1.size() << endl;
    cout << ivec1.capacity() << endl;
    cout << " jixu tian jia " << endl;
    //继续添加新的元素值,此时vector空间已经满
    ivec1.push_back(1);
    cout << ivec1.size() << endl;
    cout << ivec1.capacity() << endl;    //100  标明每当Vector容器不得不重新分配新的存储空间的时候,以加倍当前容量为其分配策略
}
结果:



容器的选用规则


1.只有Vector容器是连续存储的,deque容器不是连续存储的。但是Vector和 deque  可以提供随机的快速访问。Deque是采用一种特殊的机制,这个后续讨论
这两个容器提供了对元素的快速随机访问。付出代价是在任意位置插入元素比在最后一个插入元素花销大。
2.list在任意位置插入元素效率相同,但是快速随机访问花销大。(也可以执行下标访问,就是花销比较大而已)
表示内存中不连续的内存区域。插入和删除不需要移动后续元素,而Vector要移动后续元素,结果效率低下。
3.deque可以提供在两边快速插入,但是在中间效率低下。它有list和 Vector的一些优点
1)和 Vector一样,在中间插入或者删除效率低下。
2)和list一样提供了在 头部的高效率 插入和删除。
3)和Vector一样,不同于list,支持 所有元素的随机访问,且效率一样。
4)在容器尾部和头部插入元素不会使任何迭代器失效,在尾部或者头部删除只会使指向删除元素的迭代器失效,在中间位置的插入或者删除,只会使指向删除元素的迭代器失效。


String


1)string 的构造方式    string类型可以视为字符容器,除去一些特殊类型,string类型提供与Vector容器相同的操作。 (不以栈方式操纵容器),先看一下它的几种构造方式
#include <iostream>
using namespace std;
int main()
{

    char  data[] = "shglsgjlsg";
    string s1("chenhaosb"); //第一种构造方式
    string s2(s1);          //第二种构造方式
    cout << s1 << endl;
    cout << s2 << endl;
    string s(5,'s');        //第三种构造方式
    cout << s << endl;

    string s3,s4;           //第四种构造方式

    string s5(data,data + 7); //第5种构造方式
    cout << s5;
    //string s6(7);           //错误的构造方式
    cin >> s3; //这种方式只能读取一个以空白字符分隔的字符串,写入s3
    cout << s3;
    getline(cin,s4);//读取一行字符,以回车为结束符
    cout << s4 << endl;

    return 0;
}
除了不支持,只有单个元素个数的容器构造方式,其余的方式都满足,
string 的 其他的构造函数
string 还另外提供了三种其他的方式构建该类对象,  1)只使用一个指针参数的构造函数,该指针指向以空字符结束的字符数组,  2) 另外一个构造函数是指向字符数组的指针 和 一个标记要复制多少字符的计时器作为参数,第二中方式不需要数组以空字符结束  string(a,n) //a为一个指针,n为 复制的 前面n个字符
string s1(s2,pos) //s2  为string对象, 复制s2从下标pos的 字符

看例子:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
    const char *s = "chenhao sb";
    char s1[] = "taotao sb";
    char no_null[] = {'h','i'};
    //构造string 1)指向一个以空字符结尾的字符数组
    string string1(s);
    cout << string1 << endl;
    string string2(s1);
    cout << string2 << endl;
    string string3(no_null); //这种方式是可以编译通过的,但是会打印一堆乱码,并且每次运行结果不相同 ?why
    cout << string3 << endl;


    string string4(no_null , 2);
    cout << string4 << endl;


    string string5(s1,6);
    string string6(s,6);
    cout << string5 << " " << string6 << endl;


    string string7(string6,2); //从string6的 下标为2的元素位置开始复制
    cout << string7 << endl;
    string string8(string6,2,2); //从string6的 下标为2的元素位置开始复制2个元素
    cout << string8 << endl;


}



2)string  添加 元素的操作
#include <iostream>
using namespace std;
int main()
{

    string s;
    s.push_back('c');  //第一种插入方式,直接在后面插入
    s.push_back('h');
    s.push_back('e');
    s.push_back('n');
    cout << s << endl;
    //s.push_front('n');  错误的构造方式 和Vector一样不支持 在头部进行插入push_front
    string::iterator its = s.begin() + 2;
    cout << *its << endl;
    //s.insert(its,'s');  //第二种插入方式,在迭代器所指的前面一个位置插入元素
    //cout << s << endl;
    //注意这里插入了一个元素,则使得 迭代器 its 失效了,则上面两句可以下面这样使用,
    its = s.insert(its,'s');

    cout << *its << endl;

    s.insert(its,5,'s');// 第二种插入方式, 返回的是void类型
    cout << s << endl;

    string s1("chenhao sb");
    string::iterator its1 = s1.begin();
    string::iterator its2 = s1.end();
    its = s.begin();
    s.insert(its,its1,its2);  // 第三种插入方式  ,返回voids
    cout << s << endl;
    return 0;
}

可以看到顺序容器支持的操作,string除了 push_front() 操作不支持,其余的都支持,这里需要有一个问题要注意的是push_back()返回的是void类型, insert(p,t)  p为迭代器,t为插入元素  则返回的 是 新添加元素的迭代器, 另外要注意的是 连续插入的时候,不要使用同一个 迭代器 ,要么 必须对这个 迭代器进行新的赋值,原因是  插入操作会使得 原先的迭代器失效。

3) string的 大小操作
#include <iostream>
using namespace std;
int main()
{

    string s("chenhao sb ");
    cout << s.size() << endl;
    cout << s.max_size() << endl;
    cout << s.empty() << endl;
    s.resize(20);
    cout << s << endl;
    s.resize(30,'t');
    cout << s.size() << endl;
    cout << s << endl;

    return 0;
}

结果:

可以看到,string  中 默认size是非常大的,另外  size 是表示 string中的 字符个数,  max_size是求的字符串中能容纳的最大字符个数。

4)string 中 访问元素
#include <iostream>
using namespace std;
int main()
{

    string s("chenhao sb ");
    cout << s[0] << endl;
    cout << s.at(0) << endl;
   // cout << s.back() << endl;   错误用法,string不支持 以栈的方式操纵容器
    //cout << s.front() << endl;
    return 0;
}

5)string 的 迭代器的使用

#include <iostream>
using namespace std;
int main()
{

    string s("chenhao sb");
    string::iterator sBegin = s.begin();
    string::iterator sEnd = s.end();  //指向 string最后一个元素的下一个位置
    cout << *sBegin << " " << *--sEnd << endl;
    while(sBegin < sEnd)  //这里 可以使用 < 操作符号  不过最好使用 !=
    {
        cout << *sBegin;
        sBegin++;
    }

    cout << endl;
    string::reverse_iterator RsBegin = s.rbegin();  //指向元素最后一个容器
    string::reverse_iterator RsEnd = s.rend();       //指向容器第一个元素的前面一个位置
    while(RsBegin < RsEnd)
    {
        cout << *RsBegin;
        RsBegin ++;
    }


    return 0;
}

6)string 的 删除操作

#include <iostream>
using namespace std;
int main()
{

    string s("chenhao sb");
    string::iterator sBegin = s.begin() ;
    string::iterator sEnd = s.end() ;
    s.erase(sBegin);
    cout << s << endl;
    s.erase(sBegin , sEnd - 1 );

    string  s1(" haohaoxuexi ");
    cout << s1 << endl;
    // s1.pop_back() << endl;  错误的操作
    s1.clear();
    cout << "qingchuhou: " << endl;
    cout << s1 << endl;

    cout << s << endl;

    return 0;
}
可以使用的 删除操作,erase()和 clear  不能以栈的方式 删除元素


例子:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
    //使用迭代器将 字符串的小写字母变为大写字母
    string s1("chenhao sb");
    string s2 = "chenhao sb";
    string::iterator its1 = s1.begin();
    while(  its1 != s1.end() )
    {
        *its1 = toupper( *its1 );
        its1++;
    }
    cout << s1 << endl;

    //用vector<char>初始话一个string
    vector< char >  s3(10,'a');
    string s4(s3.begin(),s3.end());
    cout << s4 << endl;

    // 使用迭代器寻找和删除string对象中的大写字符
    string::iterator itsDel = s1.begin();
    while( itsDel != s1.end() )
    {
        if( isupper( *itsDel ) )
           itsDel = s1.erase(itsDel); //返回删除元素的下一个元素的位置给itsDel

    }
    cout << s1 << endl;
    return 0;
}

7) string 的特殊操作

substr() 求 子串
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
    string s("chenhao 2B");
    string s1 = s.substr(6,4);  //第一种方式, 从第6个位置下标开始  返回4个字符
    cout << s1 << endl;
    string s2 = s.substr(1);     //第二种方式, 从第1个位置下标开始  一直到末尾 将这些字符返回

    cout << s2 << endl;
    string s3 = s.substr();     //返回整个字符串
    cout << s3 << endl;
}
append 和 replace  函数
 用于在制定的string对象中添加字符,append  相当与 insert  在尾部 进行插入
replace函数 相当与删除一段指定范围的字符,然后在删除的位置上插入一组新字符,等效于erase 和 insert  ,不要求删除的文本长度和插入的相同。 前两个参数 执行 删除的元素范围,后面的参数用于指定插入的新字符。
int main()
{
    string s("taotao ");
    s.append("is a sb");
    cout << s << endl;
    s.insert( s.size(), "is a sb" );
    cout << s << endl;
    s.replace( s.begin(), s.end(), "hehe" ); //第一种方式
    cout << s << endl;
    s.replace( 0,4,"chenhao sb");//删除第0 到4个元素,是由下标和计数器来删除确定位置的元素
    cout << s << endl;
}

find()函数
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
    string s("hehe ,taotao is a  da sb ,taotao is and so does chenhao");
    cout << s.find("taotao") << endl;//子字符串第一次出现的位置下标
    cout << s.rfind("taotao") << endl;//子字符串最后一次出现的位置下标

    //找到子字符串中任意一个字符在源字符串中出现最早的位置
    cout << s.find_first_of("taohe") << endl;
    //找到子字符串中任意一个字符在源字符串中出现最晚的位置
    cout << s.size() << endl;
    cout << s.find_last_of("ha") << endl;   //下标从0开始计数

    //查找第一个不属于子字符串的 字符的下标
    cout << s.find_first_not_of("hehe") << endl;

    //查找最后一个不属于子字符串的 字符的下标
    cout << s.size() << endl;
    cout << s.find_last_not_of("hehe") << endl;
    return 0;
}

find 操作的返回类型  是 string::size_type类型的,使用该类型的对象存储find的返回值,声明为int 是错误的。Find  函数中可以 添加另外一个参数 ,就是pos  表示 从源字符串pos这个位置开始查找匹配,如果没有这个参数  则默认为0.

#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
    string s("hehe ,taotao is a  da tao  sb ");
    string::size_type pos = 0;
    //string::npos  表示 find中 没找到的返回的一个值,如果使用find 没找到就会返回npos
    while( (pos = s.find_first_of("tao",pos)) != string::npos )
    {
        cout << "found number at index : " << pos << " element is " << s[pos] << endl;
        ++pos;
    }

    string  s1("taotao is sb taotao");
    string::size_type first_pos = s1.find("tao");
    //返回字串 出现的最后一个位置
    string::size_type last_pos = s1.rfind("tao");
    cout << s1.size() << endl;
    cout << s1.find_last_of("tao") << endl;
    cout << first_pos << " " << last_pos << endl;

}

find_last_of  寻找 源字符串中 能 和子字符串中 任意字符匹配的   最后一个字符。
find_last_not_of 寻找源字符串中 最后一个 不能 和 子字符串中 任意字符匹配的 字符。


C++primer  习题9.39
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
   string line1 = "we were her pride of 10 she named us";
   string line2 = "Benjamin, Phoenix, the Prodigal";
   string line3 = "and perspicacious pacific Suzanne";
   string sentence = line1 + ' ' + line2 + ' ' + line3;
   string separators(" \t:,\v\r\n\f");  //分隔字符
   string word;
   //最长,最短 单词 和 下一个单词的长度, 单词的数目
   string::size_type maxLen,minLen,wordLen,count = 0;
   //存放最长或者最短单词的容器
   vector< string > longestWords,shortestWords;
   //开始位置和结束位置
   string::size_type startPos = 0, endPos = 0;

   //循环处理 sentense中的每一个单词
   while( ( startPos = sentence.find_first_not_of( separators, endPos ) ) != string::npos )
   {
        ++count;//单词数目加上1
        endPos = sentence.find_first_of( separators, startPos );

        if( endPos == string::npos )//最后一个单词
        {
            wordLen = sentence.size() - startPos;
        }
        else
        {
            wordLen = endPos - startPos;
        }
        //获取单词
        word.assign( sentence.begin() + startPos, sentence.begin() + startPos + wordLen );

        //修改下一次的访问地址
        startPos = sentence.find_first_not_of( separators, endPos );

        if( count == 1)//如果找到的是第一个单词
        {
            maxLen = minLen = wordLen;
            longestWords.push_back( word );//最长单词进入
            shortestWords.push_back( word );//最短单词进入
        }
        else//如果不是第一个单词
        {
            if( wordLen > maxLen )
            {
                maxLen = wordLen;
                longestWords.clear();//清空 容器
                longestWords.push_back( word );//最长单词进入
            }
            else if( wordLen == maxLen)
            {
                longestWords.push_back( word );//最长单词进入
            }

            if( wordLen < maxLen )
            {
                minLen = wordLen;
                shortestWords.clear();//清空 容器
                shortestWords.push_back( word );//最长单词进入
            }
            else if( wordLen == minLen)
            {
                shortestWords.push_back( word );//最长单词进入
            }

        }
   }

   cout << " word count " << count << endl;
   vector< string >::iterator iter;
   cout << "the longest word  is :" << endl;
   iter = longestWords.begin() ;
   while( iter != longestWords.end() )
   {
        cout << *iter << endl;
        iter++;
   }
    cout << "the shortest word  is :" << endl;
   iter = shortestWords.begin() ;
   while( iter != shortestWords.end() )
   {
        cout << *iter << endl;
        iter++;
   }


}
运行结果:




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值