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 序列式容器
序列式容器用来维护一组排序有序,类型相同的元素。
网友评论