Essential Cpp 笔记 上篇 2019-11-23

2019-11-23  本文已影响0人  小老鼠oo

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个泛型算法:

3.3 所有容器的通用操作

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

3.4 Sequentail Containers 序列式容器

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

上一篇下一篇

猜你喜欢

热点阅读