C++程序设计语言(特别版):第三章 标准库概念

来源:互联网 发布:sql readuncommitted 编辑:程序博客网 时间:2024/05/16 00:26

第三章   标准库概念

3.1  引言

没有任何一个重要程序是只用赤裸裸的程序设计语言写出的,首先总是要开发出一组支撑库,这也就形成了进一步工作的基础。

3.2  Hello, world!

来自main()的非0值表示出错。

fyhui:如果返回的是非0值,对程序运行或编译有什么影响?有待验证。

 

#include <iostream>             //VC6.0中,这几行代码能编译通过吗?不加其他头文件

int main()

{

       std::cout<<”Hello, world!/n”;

}

3.3  标准库名字空间

       #include <string>

       using namespace std;

       string str = “fyhui”;      

       #include是类似宏定义代码替换,把头文件代码复制一份。而namespace也是声明,它是代码复制吗?不同文件间如何使用的?与#include有何异同?

3.4  输出

       默认情况下,送到cout的输出值都将被转换为字符的系列。

       字符常量被输出为一个字符,而不是一个数值。

       一个输出表达式的结果本身还可以用于进一步的输出。ècout::operator<<返回自引用?

      

       int    a = 10;

       char c = ‘c’;

       cout<<”a = ”<<a<<” c = ”<<c<<endl;

 

3.5  字符串

       string str = “fyhui”;              int n = sizeof(string)=16;   int m = sizeof(str)=16;

       string具体有哪些成员函数和成员变量?是以‘/0’结尾吗?

 

       fyhui1i++++ii += 1i = i + 1效率排序,他们具体使用到几个寄存器和加法器?

       ++i 等价于 i = ( i, i + 1);     èè返回+1值,返回的是自身对象,最后自身已经+1

       i++ 等价于 i = (t = i, i += i, t);    èè返回原值,返回的是另一个对象,最后自身+1

 

网上流行string类的几个基本函数的实现。

       class string     //怎么没有无参数的构造函数,是因为有默认参数值的通用构造函数吗?

{

       string(const char *str=NULL);                          //通用构造函数

       string(const string &another);                                   //拷贝构造函数

       ~string();                                                         //析构函数

string & operator= (const string &other);           //赋值函数

       private:

              char        *m_data;

};

 

string::string(const char *str)

{

       if(str == NULL)     //构造空字符串

{

       m_data = new char[1];         //若能加NULL判断更好

       m_data[0] = 0;                            //*m_data = ‘/0’;

}

else

{

       m_data = new char[strlen(str) + 1];            //若能加NULL判断更好

       strcpy(m_data, str);

}

}

string::string(const string &another)

{

       assert(another.m_data != NULL);                       //没有此句林锐

       m_data = new char[strlen(another.m_data) + 1];        //若能加NULL判断更好

       strcpy(m_data, another.m_data);

}

string:~string()

{                                               //仅一行delete [] m_data;—林锐 

       if(m_data != NULL)

{

       delete [] m_data;           //由于m_data是内部数据类型,可以直接delete m_data

       m_data = NULL;

}

}

string & string::operator=(const string &other)

{

       if(this == &other)          //自赋值

{

       return *this;

}

delete [] m_data;           //释放以前资源

assert(other.m_data != NULL);     //没有这一行林锐

       m_data = new char[strlen(other.m_data) + 1];    //若能加NULL判断更好

       strcpy(m_data, other.m_data);

 

return *this;

}

3.5.1      C风格的字符串

       一个C风格的字符串是以0字符结束的字符数组。很容易将一个C风格的字符串放进string里;要调用以C风格的字符串为参数的函数,就必须以C风格字符串的形式提取string的值,函数c_str()正是。printf(“name: %s/n”, name.c_str());

3.6  输入

cin是标准输入流。

int           a;

double     d;

cin>>a>>d;            //输入32.12  63会产出错误吗?

cout<<a<<” ”<<d<<endl;

输入对应的数据类型有什么限制,中间用什么分开数据,空格符、回车换行符?

输入32.12 63àà输出32 .12;

输入w  3àà输出-858993460  -1.07374e+008

 

       按照默认方式,一个空白符,例如空格符(还有其他符号是空格符?),将结束一次输入。

       可以用getline()读取一整行的输入。一行,无论多少个字符,只要没有回车+换行?

3.7  容器

       一个以保存一批对象为主要用途的类通常被称为一个容器。

3.7.1      向量——vector

struct Entry

{

       string     name;

       int          number;

};

 

内部数据具有固定的内存,分配太大浪费,分配太小又溢出。vector是动态变长数组

vector<Entry>book(1000);           //1000个元素的向量

vector<Entry>book[1000];           //1000个空向量的数组

3.7.2      范围检查

       标准库的vector并不提供对区间范围的检查。——不是动态数组吗?只要保证访问size()以下的就不可能超出范围啊。

3.7.3      表——list’

       双向链表。如果对电话薄的插入和删除操作很频繁,请使用list,如果使用list,将倾向于不采用通过下标方式访问。——是不能下标访问吧。

list<Entry>phone_book;

3.7.4      映射——map

支持插入、删除和基于值的检索。类似于对偶的list,存储方式是数组还是链表

map<string, int>phone_book;

3.7.5      标准容器

vectorlistmap比较:对向量的下标操作开销较小且易于操作,而在向量的两个元素间插入元素代价高昂;list性质恰好相反;map类似于(关键码,值)对偶的表,除此之外还针对基于关键码去查询值做了特殊的优化。

标准库容器的总结

vector<T>                  变长向量

list<T>                     双向链表

queue<T>                   队列

stack<T>                    堆栈

deque<T>                   双向队列

priority_queue<T>            按值排序的队列

set<T>                      集合

multiset<T>                  集合,值可能重复

map<key, value>              关联数组

multimap<key, value>          关联数组,关键字可以重复

 

3.8  算法

3.8.1      迭代器的使用

template<class C, class T>int count(const C &v, T val)            //val值在v出现的次数

{

       typename C::const_iternator i = find(v.begin(). v.end(), val);

       int    n = 0;

       while(i != v.end())

{

       ++n;

       i = find(i+1, v.end(), val);

}

return n;

}

       但是我们并不需要去定义count模板,为了达到完全的通用性,标准库的count以一个系列作为参数,而不是容器作为参数。采用系列的写法,将使我们能把count用于内部数组,还可以对容器中的一部分做统计。

void g(char cs[], int sz)

{

       int i1 = count(&cs[0], &cs[sz], ‘z’);           //数组中的’z’

int i2 = count(&cs[0], &cs[sz/2], ‘z’);              //数组前一半中的’z’

}

3.8.2      迭代器类型

       各种容器还应该实现对应的迭代器?

3.8.3      迭代器I/O

       ostream_iterator<string> oo(cout);              // ostream_iterator关键字何意?

       istream_iterator<string> ii(cin);            // istream_iterator关键字何意?

 

int    main()

{

       *oo = “Hellow”;            //意思是cout<<”Hellow”;

 

       string s = *ii;                //意思是string s;   cin>>s;

}

 

 

3.8.4      遍历和谓词

       用于控制算法的函数被称为谓词,谓词将被针对每个元素调用并返回布尔值,算法根据这种返回值去执行自己预定的动作。

       bool gt_42(const pair<const string, int>& r)

{

       return r.seconde > 42;

}

 

void f(map<string, int>& m)

{

       typedef map<string, int>::const_iterator       M1;

       M1 i = find_if(m.begin(), m.end(), gt_42);

}

3.8.5      使用成员函数的算法

有许多算法都是将某个函数应用于一个系列里的各个元素。

       void draw(Shape *p)

{

       p->draw();

}

void f(list<Shape *>& sh)

{

       for_each(sh.begin(), sh.end(), mem_fun(&Shape::draw));

}

3.8.6      标准库算法

标准库算法的选择

for_each()                  对每个元素调用函数

find()                      找出参数的第一个出现

find_if()                    找出第一个满足谓词的元素

count()                     统计元素出现的次数

cout_if()                    统计与谓词匹配的元素

replace()                    用新值取代元素

replace_if()                  用新值取代满足谓词的元素

copy()                      复制元素

unique_copy()                复制元素、不重复

sort()                       对元素排序

equal_range()                找出所有具有等价值的元素

merge()                     归并排序的序列

这些算法以及其他许多算法都可以用于容器、string和内部数组的元素。

3.9  数学

3.9.1      复数

3.9.2      向量算术

3.9.3      基本数值支持

3.10      标准库功能

       标准库所提供的功能可以分类如下:

       [1]基本运行支持(例如存储分配和运行时类型信息等);

       [2]C标准库(做了极少的修改,以便尽可能地减少其中违反类型系统的情况);

 [3]字符串和I/O流;

 [4]容器和使用容器的算法的框架(即STL);

 [5]对数值计算的支持。

3.11       忠告

      [1]不要像重新发明车轮一样重复做每件事,去使用库;

[2]不要相信奇迹,要理解你的库能做什么,它们如何做的,它们做时需要多大的代价;

[3]当你遇到一个选择时,应该优先选择标准的库而不是其他的库;

[4]不要认为标准库对应任何事情都是最理想的;

[5]切记#include你所用到的功能的头文件;

[6]标准库的功能定义在名字空间std中;

[7]请用string,而不是char*

[8]如果怀疑,就用一个检查区间范围的向量;

[9]vector<T>list<T>map<keyvalue>都比T[]好;

[10]如果向一个容器中添加一个元素,用push_back()back_insert()

[11]采用对vectorpush_back(),而不是对数组realloc()

[12]main()中捕捉公共的异常。