美文网首页
Essential Cpp 笔记 上篇 2019-11-23

Essential Cpp 笔记 上篇 2019-11-23

作者: 小老鼠oo | 来源:发表于2019-11-23 22:39 被阅读0次

    Essentail C++ Stanley B.Lippman 侯捷


    1.Basic C++ Programming

    1.2 对象的定义和初始化

    int num_tries = 0;
    

    构造函数语法constructor syntax:

    int num_tries(0);
    

    以assignment = 来初始化沿袭自C,如果对象需要多个初始值就要用constructor init syntex:

    #incklude <complex>
    complex<double> purei(0, 7);
    

    complex是一个template class模板类,template class允许我们在 不必指明data members的类型 的情况下定义class,直到使用template class时才决定真正的数据类型。前面先安插一个代名,稍后才绑定实际的数据类型,这里就是绑定到了双精度浮点数double类型。

    运算符优先级precedence

    优先级从高到低排序:
    逻辑 NOT(!)
    算数 *, /, %
    算数 +, -
    关联 <, >, <=, >=
    关联 ==, !=
    逻辑 AND(&)
    逻辑 OR(|)
    赋值assignment =

    1.5 Arrays和Vertors

    Cpp允许以内建的array或者标准程序库提供的vector类来定义容器,一般建议用vector。
    定义vector object,要先include vectore head file,vector是一个class template,所以要在类名称后的<>里面指定type。size写在小括号,但是不一定是常量表达式。

    #include <vector>
    vector<int> peel_deq( seq_size );
    

    指定容器的某个位置,进而存取该位置上的元素,索引操作indexing通过[]完成。

    1.7 文件读写


    2 Procedural Programming 面向过程编程

    Pass by Reference

    传引用直接传入地址,速度更快。

    void display( const vector<int> &vec )
    {
        for( int ix = 0; ix < vec.size(); ++ix)
        {
            cout << vec[ix] << ' ';
        }
    }
    

    或者传指针:

    void display(const vector<int> *vec)
    {
        if( !vec ) {
        }
        for( int ix = 0; ix < vec->size(); ++ix )  //指针取函数的用法是vec->size()
            cout << (*vec)[ix] << ' '; //指针用索引的方式,先(*vec)得到目标。
    }
    int main()
    {
        int ia[8] = { 8, 32, 3, 13, 1, ...};
        vector<int> vec( ia, ia+8);
        display(&vec);  //传址
    }
    

    动态内存管理

    new Type;
    new Type( init_value );
    

    Type可以是内建类型,也可以是class类型。如:

    int *pi;
    pi = new int(1024);
    

    在heap分配一个int对象,再把地址赋给了pi,对象的值初始化为1024。
    从heap分配数组:

    int *pia = new int[24];
    

    数组有24个整数,cpp没有提供能 设定heap分配的数组的初始值 的语法。

    delete pi;
    delete [] pia;
    

    编译器会自动检查pi != null,如果不释放就会memory leak

    2.3 提供默认参数值Default Parameter Values

    用户可以传ofil进来输出信息,也可以不传。

    void bubble_sort(vector<int> &vrc, ofstream *ofil = 0)
    {
        for(int ix = 0; ix < vec.size(); ++ix) 
        {
            for(int jx = ix+1; jx < vrc.size(); ++jx )
            {
                if( vec[ x] > vec[jx] )
                {
                    if(ofil != 0)
                         (*ofil) << "debuf info" << endl;
                    swap(vec[ix], vec[jx], ofil);
                }
            }
        }
    }
    
    int main()
    {
        int ia[8] = {8, 32, ....};
        vector<int> vec(ia, ia+8);
    
        ofstream ofil("data.txt");
        bubble_sort(vec, &ofil);
        display(vec, ofil);
    }
    

    也可以修改输出位置:

    void display(const vector<int> &vec, ostream &os = cout)
    {
        ....
    }
    

    通常函数声明会被置于头文件,函数的定义置于代码文件,这个文件只被编译一次,要使用它时它会被link到我们的程序。
    头文件可以为函数带来更高的可见度visiblity

    //NumericSeq.h
    void display(const vector<int>&, ostream & = cout);
    
    //.c
    #include "NumericSeq.h"
    void display(const vector<int> &vec, ostream &os)
    {
        ...
    }
    

    2.5 inline函数

    适合声明为inline的函数:体积小,常常被调用,计算不复杂。
    inline函数的定义一般在头文件中,编译器会在它被调用时展开,所以其定义必须有效。

    2.6 Overloaded Func重载

    编译器可以根据参数表选择函数,但是不能根据返回值类型判断使用的函数。

    2.7 Template Funcions

    我们需要一种机制,让单一函数的内容与希望显示的各种vector类型bind起来,function template提供了这种机制。
    function template将参数表中指定的所有或者部分参数的类型信息抽离出来。
    function template以关键词template开场,后面跟尖括号<>,里面有一个或者多个识别名称,用以表示延缓决定的数据类型。
    当用户利用这个template产生函数时就必须提供确实的类型。所以识别名称扮演着置物箱的橘色,用来放置函数参数表和函数主体中的某些实际数据类型。

    template <typename elemType>
    void display_message(const string &msg, const vector<elemType> &vec)
    {
        cout << msg;
        for( int ix = 0; ix < vec.size(); ++ix )
        {
            elemType t = vec[ix];
            cout << t << ' ';
        }
    }
    

    关键字typename表示:elemType在display_message()函数中是一个临时放置类型的代称。
    elemType只是一个任意名称,也可以用foobar或者T之类。
    如何使用:

    vector<int> ivec;
    string msg;
    //...
    display_message(msg, ivec);
    

    这时,编译器会将elemType bind为int类型。然后产生一份display_mesage()函数实体。
    也可以:

    vector <string> svec;
    

    2.8 Pointers to Funuctions带来更大的弹性

    2.9 头文件

    每个函数调用另一个函数前都要声明后者,为了方便把函数声明置于头文件。这样维护一份声明就可以。
    但是,下面在.h中的声明不是很正确:

    const int seq_cnt = 6;
    const vector<int>* (*seq_array[seq_cnt])( int );
    

    这里会被解读为seq_array的定义,而不是声明。只要他前面加上关键词extern,就成为一个声明:

    extern const vector<int>* (*seq_array[seq_cnt])(int);
    

    为什么seq_cnt不需要关键字extern?
    因为const object与inline函数一样,是一次定义规则下的一个例外,const object的定义只要一出文件外就不可见了,所以可以再多个代码文件定义而不对error。
    seq_array不是const object,他是指向const object的指针。


    3 Generic Programming 泛型

    Standard Template Library STL 组成:
    1,container 容器:vector, list, set, map等类
    2,generic algorithm 泛型算法:find(), sort(),replace(),merge()

    vector与list是 sequential container 序列式容器。序列式容器会依次维护1,2,...到最后一个元素。我们在序列式容器上要进行的主要是iterate迭代操作。

    map和set是associative container 关联式容器,关联式容器可以快速寻找元素值。
    map,就是一对对key/value组合。Key用于搜寻,value用来表示读写的数据。如用户名是key,value与电话号关联。
    set,仅含有key,我们对它查询,是为了判断某值是否存在于其中。如要建立一组索引表,来记录新闻中的字,希望将the,and,but排除掉。在一个字进入索引表前,先查询excluede_word这个一个set,如果在其中就忽略它。反之加入索引表。

    3.1 Arithmetic of Pointers 指针的算术运算

    find()可以同时处理vector和array,
    但是list容器不同,list的元素以一组指针链接lined,forward指针寻址next元素,backward指针寻址preceding元素。
    所以指针的算数运算不适用于list,前者假定元素都在连续空间存储,才能根据元素大小找到下一个元素。这是现在find()最基本的假设。

    3.2 了解Iterators 泛型指针

    first和last都是list的iterators(应该是迭代器),可以这样写:

    //first last都是iterator class objects
    while(first != last)
    {
        cout << *first << ' ';
        ++first;
    }
    

    这就像指针的用法,不同的是dereference *,inequality !=,increment++,仍然由iterator classed内相关的inline函数提供。
    对list iterator而言,其递增函数会沿着list的指针前进到下一个元素,
    对vector iterator而言,它前进到下一个元素的方式,是将当前的地址加上一个元素的大小。

    第四章看如何实现iterator classes,比如如何为特定的运算符提供实现内容。本节看看如何定义和使用标准容器的iterators。
    如何取得iterators?每个标准容器都有begin(),可返回一个iterator,指向第一个元素。end()指向最后一个元素的下一个位置。
    下面是对iterator进行assign赋值,compare,increment,dereference:

    for(iter = sevc.begin(); iter != svec.end(); ++iter)
        cout << *iter << ' ';
    

    定义iterator前,看想想他应该提供什么:
    1,迭代对象(某个容器)的类型,来决定如何存取下个元素
    2,iterator所指的元素类型,来决定iterator 解引用操作的返回值
    所以iterator可能的定义形式,就是讲上述2个类型作为参数,传给iterator class:

    iterator<vector, string> iter;  //STL不是这么做的
    

    实际的语法更复杂,提供了更优雅的解法。

    //标准库的iterator语法
    //iter指向一个vector,后者的元素类型是string
    //iter指向svec的1st元素
    vector<string>::iterator iter = svec.begin();
    

    iter被定义为一个iterator,指向一个vector,后者的元素类型是string,初值指向svec的第一个元素,
    双冒号:: 表示iterator是位于string vector定义式内的嵌套nested类型,
    对于const vector:const vector<string> cs_vec;
    使用const_iterator进行遍历:vector<string>::const_iterator iter = sc_vec.begin();

    想用iterator取得元素值,可以用一般指针的解引用方式:*iter
    也可以:iter->size()

    重新设计display(),用iterator取代原来的subscript下标运算符:

    template <typename elemType>
    void display(const vector<elemType> &vec, ostream &os)
    {
        vector<elemType>::const_iterator iter = vec.begin();
        vector<elemType>::const_iterator end_it = vec.end();
    
        for( ; iter != end_it; ++iter)
            os << *iter << ' ';
    }
    

    重新设计find(),同时支持:一对指针,或者是一对指向某种容器的iterators:

    template <typename TteratorType, typename elemType >
    IteratorType
    find( IteratorType first, IteratorType last, const elemType &value )
    {
        for(; first != last; ++first)
            if(value == *first)
                return first;
        return last;
    }
    

    使用find:

    cosnt int asize = 8;.
    int ia[asize] = {1, 1, 2, 3, 5, 8,...};
    
    vector<int> ivec(ia, ia+asize);
    list<int> ilist(ia, ia+asize);
    
    int *pia = find(ia, ia+asize, 1024);
    if( pia != ia+asize )  //找到了...
    
    vector<int>::iterator it;
    it = find(ivec.begin(), ivec.end(), 1024);
    
    list<int>::iterator iter;
    iter = find(ilist.begin(), ilist.end(), 1024);
    

    如果底部元素所属类型没有提供equality = 运算符,或者用户希望赋予equality不同的意义怎么办?
    1,传入函数指针,代替原本固定使用的equality运算符
    2,使用fucntion object,这是一种特殊的class,
    下面会将find()改进为泛型算法,标准库提供的find_if()能接受函数或者function object来取代底部元素的 = ,提高弹性。

    大概有75个泛型算法:

    • search algorithm : find, count, adjacent_find, find_if , cont_if, binary_search, find_first_of
    • sorting和ordering次序整理算法: merge, partial_sort, partition, random_shuffle, reverse, rotate, sort
    • copy, deletion, substitution替换算法: copy, remove, remove_if, replace, replace_if, swap, unique
    • relational : equal, includes, mismatch
    • generation and mutation质变: fill, for_each, generate, tranform
    • numeric数值算法: accmulate, adjacent_difference, partial_sum, inner_product
    • set集合算法: set_unio, set_difference

    3.3 所有容器的通用操作

    所有容器类和string类的通用操作:

    • equality == 和 inequality != 运算符
    • assignement = ,将某个容器复制给另一个容器
    • empty,容器没有元素返回true
    • size,
    • clear, 删除元素
    • begin,返回一个iterator指向容器的1st元素
    • end
    • insert,
    • erase
      insert和erase的行为在sequential循序式与associative关联式之间有所不同。

    3.4 Sequentail Containers 序列式容器

    序列式容器用来维护一组排序有序,类型相同的元素。

    相关文章

      网友评论

          本文标题:Essential Cpp 笔记 上篇 2019-11-23

          本文链接:https://www.haomeiwen.com/subject/qrbhwctx.html