zoukankan      html  css  js  c++  java
  • 《C++ Primer Plus》学习笔记10

    《C++ Primer Plus》学习笔记10

    <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    第16章 string类和标准模板库
    主要内容:
    1)标准的C++string类
    2)auto_ptr模板
    3)标准模板库(STL)
    4)容器类
    5)迭代器
    6)函数对象
    7)STL算法
    <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    1、string类
    1)构造字符串

    //初始化为常规的C-风格的字符串
    string one ("Lottery Winner!");
    
    //初始化为由20个$字符组成的字符串
    string two(20, '$'); 
    
    //复制构造函数将string对象three初始化为string对象one
    string three(one); 
    
    //重载的+=操作符将字符串”Oops!“附加到字符串one的后面
    one += “ Oops!“; 
    
    //+=操作符能够被多次载入。以便能够附加string对象和单个字符
    one += two;
    one += '! ';
    
    //=操作符也能够被重载。以便能够将string对象、C——风格字符串或char值赋给string对
    two = "Sorry! That was";
    two = one;
    two = ‘?’;
    
    //+操作符创建了一个暂时string对象,之后使用重载的=操作符将它赋给对象four
    string four;
    four = two + three;
    
    //构造函数将一个C风格字符串和一个整数作为參数。当中的整数參数表示要复制多少个字符   
    char alls[] = "All's well that ends well";
    string five(alls, 20);
    
    //构造一个模板參数意味着包括begin不包括end在内的区间,记住最后一位指向的是后面的空格
    template<class Iter>string(Iter begin, Iter end);
    string six(alls + 6, alls + 10);
    string seven(&five[6], &five[10]);
    

    2)string类输入
    补充:
    cin

    1)输入一个数字
    2)接受一个字符串。遇到空格、TAB、回车都结束
    cin.get()
    1)cin.get(ch)用来接收字符

    char ch; cin.get(ch);
    

    2)cin.get(s,n)用来接收一行字符串。能够接收空格 这个类似于getline 能够输入多个单词用空格隔开

    char a[20]; cin.get(a,20);
    

    cin.getline()
    接受一个字符串,能够接收空格。

    这个和cin(s,n)都须要注意

    char m[20] = jklkjilj;
    cin.getline(m,5);
    cout << m << endl; //这里输出结果为jklk 由于最后一个字符为‘0’
    

    这就能够知道直接上cin.getline()有三个參数,接收字符串到m,接收个数,结束符(当省去的时候我们就默觉得''或者‘/n’)

    char a[20]; cin.getline(a,5);
    

    getline()
    接收一个字符串,能够接收空格并输出,须要包括头文件#include<string>
    注意,getline()属于string流。所以仅仅有把字符串定义为string型,我们才干够用。否则仅仅能用istream流的cin.getline(m,20)
    gets()
    接收一个字符串,能够接收空格并输出。可是须要加上头文件#include<string>
    并且须要注意不能写成m = gets();应该为gets(m)
    getchar()
    接收一个字符。须要有头文件#include<string>
    这几须要注意,不能写成getchar(ch);应该写成ch = getchar(); getchar()是C语言的函数,C++也兼容,可是少用。
    综上所述。依据我的习惯,C++以后碰到须要输入带空格的字符时用char ch; cin.get(ch)
    字符串#include<string> gets(str)

    3)使用字符串

    string str1("cobra");
    string str2("coral");
    str1.length() == str2.size
    

    确定字符的长度size()和length()成员函数都返回字符串中的字符数;length来自于较早版本号的string类。size()则是为提供STL兼容性而加入的
    find的四个版本号 string::npos 是字符串可储存的最大字符数
    size_type find (const string &str, size_type pos = 0) const
    从字符串的pos開始,查找子字符str。假设找到,则返回该子字符首次出现时其首字符的索引,否则返回string:: npos
    size_type find(const char *s, size_type pos = 0) const
    从字符串的pos開始。查找子字符串s,假设找到,则返回该子字符首次出现时其首字符的索引,否则返回string:: npos
    size_type find(const char *s, size_type pos = 0, size_type n)
    从字符串的pos位置開始,查找s的前n个字符组成的子字符串,假设找到。则返回该子字符串首次出现时其首字符的索引,否则返回string::npos
    size_type find(char ch, size_type pos=0) const
    从字符串的pos位置開始,查找字符ch,假设找到,则返回该字符首次出现的位置,否则返回string::npos
    4)string还提供了哪些功能
    ①自己主动调整大小的功能
    每当程序将一个字符附加到字符串末尾时将发生什么呢?
    不能只将已有的字符串加大了,由于相邻的内存可能被占用了,因此可能须要一个新的内存块,并将原来的内容拷贝到新的内存单元中。


    方法capacity()返回当前分配给字符串的内存块的大小,而reseve()方法让您可以请求内存块的最小长度
    2、auto_ptr类
    是一个模板类,用于管理动态内存分配的使用方法。
    auto_ptr模板定义了类似指针的对象,能够将new获得(直接或者间接)的地址赋给这样的对象。当auto_ptr对象过期时候。其析构函数将使用detele来释放内存,因此假设将new返回的地址赋给auto_ptr对象时。无须记住稍后释放这些内存,它过期之后,会自己主动释放。
    *使用auto_ptr类注意事项:
    1)记得要包括memory头文件#include<memory>
    2)仅仅能对new分配的内存使用auto_ptr对象,而不要对由new[]分配的或通过声明变量分配的内存使用它。

    auto_ptr<int> pi (new int [200]); //No
    

    3、STL
    STL提供了一组表示容器、迭代器、函数对象、算法的模板。
    容器是一个与数组类似的单元,可以存储若干个值;算法是完毕特定任务的处方。迭代器可以用来遍历容器的对象。与可以遍历数组的指针类似。是广义指针;函数对象是类似于函数的对象。可以是类对象或函数指针
    1)vector模板类

    #include vetor
    using namespace std;
    vector<int> ratings(5);//5个整型的矢量
    int n;
    cin >> n;
    vector<double> scores(n);//n个double的矢量
    ratings[0] = 9;
    for(int i = 0; i < n; i++)
        cout << scores[i] << endl;
    

    2)可对矢量运行的操作

    size()——返回容器中元素的数目
    swap()——交换两个容器的内容
    begin()——返回一个指向容器中第一个元素的迭代器
    end()——返回一个表示超过容器尾的迭代器
    

    迭代器。要为vector的double类型规范声明一个迭代器

    vector<double>::iterator pd;
    vector<double> scores;//scores是一个vector<double>对象
    pd = scores.begin();//初始地址
    *pd = 22.3;//第一个元素的值
    ++pd;
    

    遍历整个容器的内容:

    for(pd = scores.begin(); pd != scores.end(); pd++)
        cout << *pd << endl;
    

    push_back()是一个方便的方法,它将元素加入到矢量末尾,它负责内存管理,添加矢量的长度。使之能容纳新的成员

    vector<double>scores;//创建一个空的向量
    double temp;
    while(cin>>temp && temp >= 0)
        scores.push_back(temp);
    cout << "You entered" << scores.size() << " scores.
    ";
    

    erase()方法删除矢量中给定区间的元素。它接受两个迭代器參数,这些參数定义了要删除的区间,第一个迭代器指向区间的起始处。第二个迭代器位于区间终止处的后一个位置

    scores.erase(scores.begin(), scores.begin() + 2);//即删除了begin()和begin()+1指向的元素
    

    记住:区间[it1, it2]由迭代器it1和it2指定,其范围为it1到it2不包含it2
    insert()方法与erase()相反,它接受3个迭代器參数,第一个參数指定了新元素的插入位置,第二个和第三个參数定义了被插入区间

    old.insert(old.end(),new.begin()+1,new.end());//将矢量new中除第一个元素以外的全部元素插入到old矢量的第一个元素的前面
    

    3)对矢量可运行的其它操作
    搜索、排序、随机排序
    for_each()函数用于很多容器类,它接受3个參数,前两个是定义容器中区间的迭代器,最后一个是指向函数的指针,被用来指向函数应用与容器区间中的各个元素,被指向的函数不能改动容器元素的值;能够用for_each循环取代for循环

    vector<Review>::iterator pr;
    for(pr = books.begin();pr != books.end(); pr++)
        ShowReview(*pr);
    

    替换为

    for_each(books.begin(),books.end()。ShowReview);//这样避免显式地使用迭代器变量
    

    Random_shuffle()函数接受两个指定区间的迭代器參数。而且随机排列该区间中的元素

    random_shuffle(books.begin(), books.end());//随机排列books矢量中的全部元素
    

    sort()也要求容器支持随机訪问

    //版本号一接受两个迭代器參数
    vector<int> coolstuff;
    ……
    sort(coolstuff.begin(),coolstuff.end());//升序
    //版本号二假设容器元素是用户定义的对象,必须定义可以处理类型对象的operator<()函数
    bool operator< (const Review & r1, const Review & r2)
    {
        if(r1.title < r2.title)
            return true;
        else if(r1.title == r2.title && r1.rating < r2.rating)
            return true;
        else
            return false;
    }
    

    有了这种函数之后。我们就能够对包括Review对象如books的矢量进行排序

    sort(books.begin(), books.end());//升序排序,全排序
    

    上述版本号是依照title成员的字母顺序排序,假设title成员同样,则依照rating排序

    bool WorseThan (const Review & r1, const Review & r2)
    {
        if(r1.title < r2.title)
            return true;
        else
            return false;
    }
    

    有了这个函数。就能够将包括Review对象的books的矢量按rating升序排列

    sort(books.begin(), books.end(), WorseThan);//升序排序,全然弱排序
    

    4、通用编程技术
    STL是一种通用编程技术,面向对象编程关注的是编程的数据方面,而通用编程技术关注的是算法,共同之处是抽象和创建可重用的代码。


    通用编程技术旨在编写独立于数据类型的代码。在C++中。完毕通用程序的工具是模板
    1)模板使得算法独立于存储的数据类型,而迭代器使算法独立于使用的容器类型。它们都是STL通用方法的重要组成部分
    2)为什么要使用迭代器?
    目的是为了编写算法时。尽可能使用要求最低的迭代器。并让它适用于容器的最大区间。这样通过使用级别最低的输入迭代器。find()函数便可用于不论什么包括可读取值的容器。sort()函数因为须要随机訪问迭代器,所以仅仅能用于支持这样的迭代器的容器。

    //在一个double数组中搜索特定值的函数
    double * find_ar(double * ar, int n, const double & val)
    {
        for(int i = 0; i < n; i++)
            if(ar[i] == val)
                return &ar[i];
            return 0;
    }
    

    使用迭代器

    typedef double * iterator;
    iterator find_ar(iterator begin, iterator end, const double & val)
    {
        iterator ar;
        for(ar = begin; ar != end; ar++)
            if(*ar == val)
                return ar;
            return end;
    }
    

    总结下STL方法,首先是处理容器的算法,应尽可能用通用的术语来表达算法,使之独立于数据类型和容器类型,为使通用算法可以适用于详细情况,应定义可以满足算法需求的迭代器,并把要求加到容器设计上,所以要设计基本迭代器的特征和容器特征。
    3)迭代类型
    ①输入迭代器——被程序用来读取容器中的信息
    单向迭代器,仅仅读。仅仅能递增,不能倒退。
    ②输出迭代器——将信息从程序传输给容器的迭代器,程序的输出就是容器的输入
    单向迭代器,仅仅写
    ③正向迭代器——使用++操作符来遍历容器。正向迭代器既能够使得能够读取和改动数据,也能够使得仅仅能读取数据

    int * pirw;//读写迭代器
    const int * pir;//仅仅能读取迭代器
    

    ④双向迭代器——具有正向迭代器全部特性,同一时候支持两种递减操作符
    reverse函数能够交换第一个元素和最后一个元素。将指向第一个元素的指针加1、将指向第二个元素的指针减去1,而且反复这样的处理过程。
    ⑤随机訪问迭代器——具有双向迭代器全部特性。同一时候加入了支持随机訪问的操作
    X表示随机迭代器类型。T表示被指向的类型,a和b是迭代器值,n为整数。r为随机迭代器变量或引用
    随机訪问迭代器操作:

    a+n 指向a所指向元素后的第n个元素
    a-n 指向a所指向元素前的第n个元素
    a[] 等价于*(a+n)
    

    4)概念、改进和模型
    概念的详细实现被称为模型
    ①将指针用作迭代器
    迭代器是广义的指针。指针满足全部迭代器的要求,迭代器是STL算法的接口,而指针是迭代器,全部STL算法可用于常规数组。
    copy()、ostream_iterator和istream_iterator
    copy() 将数据从一个容器拷贝到还有一个容器中

    int casts[10] = {6, 7, 2, 9, 4, 11, 8, 7, 10, 5};
    vector<int> dice[10];
    copy(casts, casts + 10, dice.begin());//前两项是复制的范围,后面是拷贝到什么位置
    

    使用注意事项:
    前两个參数最好是输入迭代器。最后一个參数最好是输出迭代器;
    copy()函数将覆盖目标容器中已有的数据,我们不能将数据放入空矢量中;
    ostream_iterator
    是输出迭代器概念的一个模型,它是一个适配器(adapter)一个类或函数,能够将一些其它接口转换为STL使用的接口。

    #include<iterator>
    ……
    ostream_iterator<int,char> out_iter(cout, " ");//第一个參数指出被发送给输出流的数据类型,第二个參数指出输出流使用的字符类型
    copy(dice.begin(),dice.end(),out_iter);
    

    istream_iterator
    是istream输入可用做迭代器接口。它是一个输入迭代器概念的模型,使用两个istream_iterator对象来定义copy()的输入范围
    copy(istream_iterator<int,char>(cin), istream_iterator<int, char>(), dice.begin());//第一个參数指出要读取的数据类型,第二个參数指出输入流使用的字符类型
    如今如果反向打印容器的内容,vector类有一个名为rbegin()的成员函数和一个名为rend()的成员函数,前者返回一个指向超尾的反向迭代器。后者返回一个指向第一个元素的反向迭代

    copy(dice.rbegin(), dice.rend(), out_iter);//反向显示内容
    

    记住rbegin()和end()返回同样的值,但类型不用(reverse_iterator和iterator)同样rend和返回同样的值(指向第一个元素的迭代器),但类型不同。


    5)序列
    6种STL容器类型,deque、list、queue、priority_queue、stack和vector都是序列,队列可以在队尾加入元素,在队首删除元素,Deque表示的双端队列同意在两端加入和删除元素。
    数组和链表都是序列,但分支结构(当中每一个节点多指向两个子节点)不是序列。

    Xa(n,t); //声明一个名为a的、由n个t值组成的序列
    X(n,t); //创建一个由n个t值组成的匿名序列
    Xa(i,j);//声明一个名为a的序列。并将其初始化为区间[i,j)的内容
    X(i,j);//创建一个匿名序列,并将其初始化为区间[i,j)的内容
    a.insert(p,t) //将t插入到p的前面
    a.insert(p,n,t) //将n个t插入到p的前面
    a.insert(p,i,j) //将区间[i,j)中的元素插入到p的前面
    a.erase(p) //删除p指向的元素
    a.erase(p,q) //删除区间[p,q]中的元素
    a.clear() //等价于erase(begin(),end())
    

    6)以下具体介绍6种序列容器类型
    vector
    vector是数组的一种类表示,它提供了自己主动内存管理功能。能够动态地改变vector对象的长度,并随着元素的加入和删除而增大和缩小,它提供了对元素的随机訪问,在尾部加入和删除袁术的时候是固定的。但在头部或中间插入和删除元素的复杂度为线性时间。


    deque
    deque模板类表示双端队列。类似于vector主要差别在于从deque对象的開始位置插入和删除元素的时间是固定的,而不像vector中那样是线性时间。
    list
    表示双向链表。出了第一个和最后一个元素外,每一个元素都与前后的元素相链接。这就意味着能够双向遍历链表。list和vector之间的差别在于list在链表中任一个位置进行插入和删除的时间是固定的。vector强调的是通过随机訪问进行高速訪问,而list强调的是元素的高速插入和删除。


    与vector相似。list也是能够反转容器的。不同的是list不支持数组表示法和随机訪问,
    queue
    queue模板的限制比deque多。它不仅不同意随机訪问队列元素,甚至不同意遍历队列。
    queue的操作

    bool empty() const //假设队列为空,则返回true;否则返回false
    size_type size() const //返回队列中元素的数目
    T& front() //返回指向队首元素的引用
    T& back()  //返回指向队尾元素的引用
    void push(const T & x) //在队尾插入x
    void pop() //删除队首元素
    

    记住假设要使用队列中的值。应首先使用front()来检索这个值。之后我们再用pop()将它从队列中删除。


    priority_queue
    还有一个适配器类。它支持的操作与queue同样。在priority_queue中。最大的元素被移到队首。


    stack
    记住在头文件里得加上#include<stack>也是一个适配器类。给底层类提供了典型的堆栈接口。

    bool empty() const //假设堆栈为空,则返回true;否则返回false
    size_type size() const //返回堆栈中元素的数目
    T& top() //返回指向栈顶元素的引用
    T& back()  //返回指向队尾元素的引用
    void push(const T & x) //在堆栈顶部插入x
    void pop() //删除栈顶元素
    

    假设要使用堆栈中的值,必须首先使用top()来检索这个值,之后使用pop()将它从堆栈中删除。


    7)联合容器
    长处在它提供了对元素的高速訪问,与序列相似,联合容器也同意插入新元素,只是不能指定元素插入位置,原由于联合容器通常包括用于确定数放置位置的算法,以便可以非常快的检索信息。
    四种:set、multiset、map、multimap
    前两个是在头文件#include<set>
    后两个是在头文件#include<map>
    set
    可反转,可排序,keyword是唯一的,所以仅仅能存储同一种类型的值

     set<string> A;
    

    将集合初始化为数组内容的简单方法:

    const int N = 6;
    string s1[N] = {"buffoon", "thinkers", "for", "heavy", "can", "for"};
    set<string> A(s1, s1 + N);//初始化 记住区间的最后一个位置是超尾,s1 + N指向数组尾部后面的一个位置。集合被排序
    ostream_iterator<string,char> out(cout, " ");
    copy(A.begin(), A.end(), out);
    

    求两个集合的并集

    set_union(A.begin(), A.end(), B.begin(), B.end(), ostream_iterator<string, char> out(cout, " "));
    

    补充:set.intersection()和set_difference()函数查找交集(两个集合公有的元素)和获得两个集合的差(第一个集合减去两个集合公有的元素)
    两个实用的set方法各自是lower_bound()和upper_bound()
    lower_bound()方法将keyword作为參数并返回一个迭代器。该迭代器指向集合中第一个不小于keyword參数的成员,相同。upper_bound()返回的位第一个大于keyword參数的成员
    multimap

    可反转,可排序,keyword是能够与多个值关联
    创建一个multimap对象,当中keyword的类型是int,所存储的值的类型为string

     multimap<int, string>codes;
    

    为了将信息结合在一起,实际的值类型将keyword类型和数据类型结合为一对,STL使用pair<class T, class U>模板类将这两种值存储到一个对象中,假设keytype是keyword类型。而打她type是被存储的数据类型

    pair<const keytype, datatype>
    

    创建一个匿名pair对象。并将它插入

    codes.insert(pair<const int, string> (213, "Los Angeles"));
    

    5、函数对象
    1)list模板中又一个将断言作为參数的remove_if()成员,该函数将断言应用与区间的每个元素假设为true,则删除这些元素。

    //删除链表three中全部大于100的元素
    bool tooBig(int n){return n > 100;}
    list<int> scores;
    ……
    scores.remove_if(tooBig);
    

    2)提前定义的函数数
    tranform()
    前两个參数是指定容器区间的迭代器,第3个參数是指定将结果拷贝到哪里的迭代器,最后一个參数是一个函数符。它被应用于区间中的每一个元素,生成结果中的新元素。

    transform(gr8.begin(), gr8.end(), out, sqrt);
    

    6、算法
    STL将算法库分成4组
    ①非改动式序列操作
    ②改动式序列操作
    ③排序和相关操作
    ④通用数字运算
    1)函数与容器的方法
    首先它更适合于特定的容器。其次作为成员函数,它能够使用模板类的内存管理工具,从而在须要时调整容器的长度。
    比如la是一个list<int>对象。使用链表的remove()方法:

    la.remove(4);//链表中全部值为4的元素将被删除
    

    另一种形式

    remove(lb.begin(), lb.end(), 4);
    

    因为该remove()函数不是成员。因此不能调整链表的长度。它将没有删除的元素放在链表的開始位置,并返回一个指向新的超尾值的迭代器。这样,便能够用迭代器来改动容器的长度
    链表中还能够使用erase来删除一个区间

    lb.erase(last,lb.end());
    

    2)使用STL

    //用于显示3个容器,包括输入内容的矢量,包括单词列表的集合和包括单词计数的映象内容.cpp
    #include <iostream>
    #include <string>
    #include <vector>
    #include <set>
    #include <map>
    #include <set>
    #include <algorithm>
    #include <iterator>
    #include <algorithm>
    #include <cctype>
    using namespace std;
    
    //忽略大写和小写,转换为小写因为tolower()函数被定义为int tolower(int)编译器希望为char,我们能够用toLower取代tolower
    char toLower(char ch)
    {
        return tolower(ch);
    }
    
    string & ToLower(string & st);
    void display(const string & s);
    
    int main()
    {
        //创建一个vector<string>对象,并用push_back()将输入的单词加入到矢量中
        vector<string> words;
        string input;
        while(cin >> input && input != "quit")
            words.push_back(input);
    
        //所有一行显示输出的单词,每一个单词空一个空格
        for_each(words.begin(), words.end(), display);
        cout << endl;
    
        //使用transform将矢量中的数据拷贝到集合中,使用一个转换函数将字符串转换成小写,而且我们要排序,且将同样的仅仅显示一次
        set<string> wordset;
        transform(words.begin(), words.end(), 
            insert_iterator<set<string>>(wordset,wordset.begin()),
            ToLower);
        for_each(wordset.begin(),wordset.end(), display);
        cout << endl;
    
        //单词和计数作为pair<const string, int>对象存储在map对象中
        map<string, int>wordmap;
        set<string>::iterator si;
        for(si = wordset.begin(); si != wordset.end(); si++)
            wordmap[*si] = count(words.begin(), words.end(), *si);
    
        for(si = wordset.begin(); si != wordset.end(); si++)
            cout << *si << ": " << wordmap[*si] << endl;
    
        return 0;
    }
    
    string & ToLower(string & st)
    {
        transform(st.begin(), st.end(), st.begin(), toLower);
        return st;
    }
    
    void display(const string & s)
    {
        cout << s << " ";
    }
    

    执行结果:



    <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<结束2014.7.24 2:50

  • 相关阅读:
    SQL2005四个排名函数(row_number、rank、dense_rank和ntile)的比较
    LINQ to List泛型的几种简单查询
    sql语句精选
    C#利用QrCode.Net生成二维码(Qr码)
    LINQ to XML CRUD,并且封装为DAL方法
    vim编辑器
    去年的烟花特别多……
    年初七
    活死人黎明 Dawn of the Dead
    在碟片里奔驰我的看碟人生
  • 原文地址:https://www.cnblogs.com/mfrbuaa/p/4606701.html
Copyright © 2011-2022 走看看