TC++PL Chapter 3 读书笔记

来源:互联网 发布:五十知天命的意思 编辑:程序博客网 时间:2024/05/01 13:20

A Tour of the Standard Library

本章中主要讲述了标准库中的一些基本应用

从Hello World!程序开始,要使用标准库需要#include 和 namespace

需要指出的是,在现在大多数的vc++版本中,形如

#include <iostream>

这种写法的时候需要使用作用域标识符 std:: 或者

using namespace std 而

#include <iostream.h>

这种写法的时候就不需要了,以免出现错误

iostream 标准输入输出流

string      字符串类型,取代了C中的char *繁琐的字符串表示

对传统的标准库作了简单的介绍

下面是STL中的内容

containers 容器

Bj的定义

A class with the main purpose of holding objects is commonly called a container.

本质就是一个类

通过一个电话本的描述,介绍了vector,list,map他们的结构和特点

首先,用一个简单的结构体的描述电话簿的成员变量

struct Entry {
string name;
int number;
};
用数组来表示电话簿

Entry phone_book[1000];
void print_entry(int i) // simple use
{
cout << phone_book[i].name << ´ ´ <<phone_book[i].number <<´/ n´;
}

出现的问题是,大小是固定的,不能动态改变,必然会造成资源浪费

用标准库提供的vector容器

vector<Entry> phone_book(1000);

void print_entry(int i)

{

    cout<<phone_book[i].name<<' '<<phone_book[i].number<<'/n';

}

void resize_entry(int n)   //增加n个电话条目的容量

{

    phone_book.resize(phone_book.size()+n);

}

需要注意的是vector的定义

vector<Entry> phone_book(1000);//1000个元素的vector

vector<Entry> phone_book[1000];//1000个空vector

还有在复制vector的时候,当vector的容量非常大的时候,将有较大的开销,这时候可以用指针或者引用

用vector表示的问题是没有越界检查range check

int i=phone_book[1001].number

i 得到的将是一个随机值,不会给出异常

重新设计一个带有越界检查的vector

template<class T> class Vec: public vector<T>{

public:

 Vec() : vector<T>(){}

 Vec(int s) : vector<T>(s){}

 T& operator[] (int i) {return at(i);}

 const T& operator[](int i) {return at(i);}

};

以上抛出越界异常的功能依赖于vector中的at()成员函数,at()返回相应的值并且带有越界检查,so如果越界则抛出out_of_range类型的异常,当然这只是为了方便说明的一种写法,实际中具体如上的的写法并无太大意义,直接用at()就行了嘛

之后用“踹”就可以看到结果

 try{
 int i=phone_book[1001].number;
 cout<<i<<'/n';
 }
 catch(out_of_range){
 cout<<"out of range/n";
 }

1000和1001的输出为out of range 而用vector在Vc++6.0上面的结果是0

try-catch是很好的书写习惯

下面用list来表示电话簿

list的好处是方便插入和删除记录

对list的访问与vector不同,访问list中的数据需要利用一个给定值对list中的数据进行查找,利用iterator进行操作非常简单

void print_entry(const string& s){
 typedef list<Entry>::const_iterator LI;
 for(LI i=phone_book.begin,i!=phone_book.end,i++){
  Entry& e=*i;
  if(s==e.name) cout<<e.name<<' '<<e.number<<'/n';
 }
 }

在不需要改变list中的数据的时候const_iterator是我们需要的,否则需要iterator,有关iterator的类型将在后面的章节讨论

增加数据

void add_entry (Entry & e , list <Entry>::iterator i )
{
phone_book.push_front (e); // add at beginning
phone_book.push_back(e); // add at end
phone_book.insert(i ,e); // add before the element ‘i’ refers to
}

用map表示电话簿

用list表示需要做一个线性查找来访问数据,这无疑是灰常低效率的

map有更好的效率并且可以方便插入和删除

map通过key进行检索

各种表示方法各有利弊,vector简单并且开销少,但是对于插入和删除开销很大,list则恰好相反,map与list类似但是采用更优的key进行检索

标准库中有更多的容器并且允许用户自己实现container。

算法

同样是STL中的内容

void f (vector <Entry>& ve , list <Entry>& le)
{
sort (ve.begin(), ve.end ());
unique_copy (ve.begin(),ve.end(), le.begin());
}

以上为标准库中的sort和unique_copy算法

一个sequence由两个iterator来表示,上面语句中ve.begin()和ve.end(),需要指出的是end表示的是最后一个元素的下一个位置

copy将会覆盖,但是不会分配空间,增加容量

在队列的尾部插入元素可以使用back_inserter() 会增加空间

几种表达式

copy (ve.begin (), ve.end(), le); // error: le不是一个迭代器

copy (ve.begin (), ve.end(), le.end()); // bad: 越界copy不会分配额外空间
copy (ve.begin (), ve.end(), le.begin()); // 将会覆盖

迭代器iterator

STL的第三个重要组成部分

这里举了一个例子,描述了一个count的算法

template<class C,class T> int count(const C& v,T val){
 typename C::const_iterator i=find(v.begin(),v.end(),val);
 int n=0;
 while(i!=v.end()){
  ++n;
  ++i;
  i=find(i,v.end(),val);
 }
 return n;
}
void main()
{
string m="Mary had a little lamb.";
int a_count=count(m,'a');
cout<<a_count<<'/n';
}

结果是4

这个count算法将同样适用于其他容器

iterator类型

随container的不同而不同

输入输出流与迭代器

输出流iterator

ostream_iterator<string> oo(cout);

int main(){

*oo="Hello ";

++oo;

*oo="World!";

}

输入流同理

int main ()
{
string from,to;
cin>>from>>to;
ifstream is(from.c_str());
istream_iterator<string> ii(is);
istream_iterator<string> eos;

vector<string> b;
copy(ii,eos,back_inserter(b));
sort (b.begin(), b.end());
ofstream os(to.c_str());
ostream_iterator<string> oo(os,"/n");
unique_copy(b.begin(),b.end(),oo);

return!is.eof () && !os ;
}

结果为

1.txt中

aaa ddd 111 333 222 bbb 111 555 ooo ddd

2.txt中

111
222
333
555
aaa
bbb
ddd
ooo
需要注意的是在vc++6.0中书中的语句

vector<string> b(ii,eos)

这种构造函数是不存在的,转化成

copy(ii,eos,back_inserter(b))

进行初始化

程序中的eos是未初始化的一个迭代器,用来表示输入队列的结尾的后一个元素,任意未初始化的该类型迭代器都可以有相同的功能

遍历与谓词,在vc++6.0中STL惨不忍睹。。。。

map<string,int> histogram;
void record(const string& s){
 histogram[s]++;
};
void print(const pair<const string,int>& r){
 cout<<r.first<<' '<<r.second<<'/n';
};
int main ()
{
 istream_iterator<string> ii(cin);
 istream_iterator<string> eos;

 for_each(ii,eos,record);
 for_each(histogram.begin(),histogram.end(),print);
}

mem_fun()机制

这种机制用于支持保存多态对象的容器应用标准算法

算法

A general definition of an algorithm is ‘‘a finite set of rules which gives a sequence of operations for solving a specific set of problems [and] has five important features:
Finiteness ... Definiteness ... Input ... Output ... Effectiveness’’ [Knuth,1968,§1.1].

给出解决一类特定问题的一个操作序列的一组有限的规则集,有五个重要的特点:

有穷性,定义性,输入,输出,效率

C++中,算法就是在一个元素序列sequence上操作的一个模板template

数学

vector不支持数学运算,可以增加,但不会是最优的

可以使用valarray

 

包含一个类到标准库里的一个主要准则就是这个类会被几乎每一个程序员用到,并且用通用的形式给出而且比起简单实现的版本不产生额外的重大的开销