C++学习笔记3:标准库类型

作者: 云中布衣   分类:  学习笔记    热度: (357℃)   时间: 2017-6-23 19:03   标签: #始于C++而不止于C++    

前面的学习笔记介绍了C++的基本数据类型。除了基本数据类型,C++还定义了一个内容丰富的抽象数据类型(abstract data type)标准库。其中最重要的标准库类型是string和vector,它们分别定义了大小可变的字符串和集合。string和vector往往将迭代器用作配套类型(companion type),用于访问string中的字符或者vector中的元素。这些标准库类型是语言组成部分中那些更基本的数据类型(如数组和指针)的抽象。另一种标准库类型bitset则提供了一种抽象的方法来操作位的集合。与整型值上的内置位操作符相比,bitset类类型提供了一种更方便的处理位的方式。

1.命名空间的using声明

前面的程序,我们通过直接说明名字来自std命名空间,来引用标准库中的名字。例如从标准输入读入数据时使用std::in。其中::操作符叫做作用域操作符,它的含义是右操作数的名字可以在左操作数的作用域中找到。因此,std::cin的意思是说所需名字cin是在命名空间std中定义的。但是通过这种符号引用标准库名字的方式是非常麻烦的。幸运的是,C++提供了更加简洁、安全的方式来使用命名空间成员---using声明。使用了using声明可以在不需要加前缀namespace_name::的情况下访问命名空间中的名字。using声明形式如下:

using namespace::name;
一旦使用了using声明,就可以直接引用名字,而不需要再引用该名字的命名空间。使用命名空间的using声明,需要注意以下两点:

1)每个名字都需要一个using声明

2)使用标准库类型的类定义。有一种情况下,必须总是使用完全限定的标准库名字:在头文件中。在头文件中放置using声明,就相当于在包含头文件的每个程序中都放置了同一using声明,不论该程序是否需要using声明。因此通常头文件中应该只定义确实必要的东西。

2.标准库string类型

string类型支持长度可变的字符串,C++标准库将负责管理与存储字符相关的内存,以及提供各种有用的操作。与其他的标准库类型一样,用户程序要使用string类型对象,必须包含相关头文件。如果提供了合适的using声明,那么编写出来的程序将变得更加简短些。

#include <string>
using std::string;
string标准库支持几个构造函数。所谓构造函数是一个特殊成员函数,定义如何初始化该类型的对象。以下是几种初始化string对象的方式,当没有明确指定对象初始化时,系统将使用默认构造函数。

几种初始化string对象的方式
string s1;
默认构造函数,s1为空串
string s2(s1);
将s2初始化为s1的一个副本
string s3("value");
将s3初始化为一个字符串字面值副本
string s4(n, 'c');
将s4初始化为字符'c'的n个副本
警告:因为历史原因以及为了与C语言兼容,字符串字面值与标准库string类型不是同一种类型!!!

string对象的读写:前面我们使用iostream标准库来读写内置类型的值,如int,double等。同样地,也可以使用标准输入输出操作符来读写string对象:

string s; //empty string
cin >> s; //read white-separated into s
cout << s << endl; //write s to output
第二条语句,从标准输入读取string,并将读入的串存储在s中。string类型的输入操作符步骤如下:
  • 读取并忽略开头所有的空白字符(如空格,换行符,制表符)
  • 读取字符直至再次遇到空白字符,读取终止

读入未知数目的string对象:和内置类型的输入操作符一样,string的输入操作符也会返回所读的数据流。因此可以把输入操作作为判断条件:

string word;
//read until end-of-file, write each word to a new line
while(cin >> word){
    cout << word <<endl;
}
用getline读取整行文本:另外还有一个有用的string IO操作(getline)。这个函数接受两个参数,一个输入流对象和一个string对象。getline函数从输入流的下一行读取,并保存读取的内容到string中但不包括换行符。getline函数将istream参数作为返回值,和输入操作符一样也可以把它用作判断条件,改写上述程序如下:
string line;
//read line at time until end-of-file
while(getline(cin, line)){
    cout << line << endl;
}
string对象的方便之处在于作用在string对象之上的相关操作,下表是常用的string操作。

string对象的操作
s.empty()
如果s为空串,则返回true,否则返回false
s.size()
返回s中字符的个数
s[n]
返回s中位置为n的字符,位置从0开始计数
s1 + s2
把s1和s2链接成一个新字符串,返回新生成的字符串
s1 = s2
把s1内容替换为s2的副本
v1 == v2
比较v1与v2的内容,相同则返回true,否则返回fasle
!=,<,<=,>,>=
保持这些操作的惯有含义
string::size_type类型:size操作返回的是string::size_type类型的值,它称为string类类型的配套类型(companion type)。通过这些配套类型,库类型的使用就能与机器无关(machine-independent)。size_type定义为与unsigned型具有相同的含义,而且可以保证足够大能够存储任意string对象的长度。任何存储string的size操作结果的变量必须为size_type类型,特别重要的是,不要把size的返回值赋给一个int变量。

和字符串字面值的连接:当进行string对象和字符串字面值混合连接操作时,+操作符的左右操作数必须至少有一个string类型。此外string标准库类型还定义加操作返回一个string对象。

string s1 = "hello";
string s2 = "world";
string s3 = s1 + ",";//ok:adding a string and a literal
string s4 = "hello" + ",";//error:no string operand
string s5 = s1 + "," + "world";//ok
string s6 = "hello" + "," + s2;//error
从string对象获取字string类型通过下标操作符([])来访问string对象中的单个字符。下标操作符需要取一个size_type类型的值。下标操作可用作左值且任何可产生整数值的表达式都可用作下标操作符的索引。
string str("some string");
for(string::size_type ix =0 ; ix != str.size(); ++ix){
    cout << str[ix] << endl;
    str[ix] = '*';
}
我们经常要对string对象中单个字符进行处理,例如,通常需要知道某个特殊字符是否为空白字符、字母或数字。这时我们就需要用到各种字符操作的函数比如isalnum(c)、isalpha(c)、ispunct(c)、islower(c)、isupper(c)、tolower(c)、toupper(c)等。这些函数都在cctype头文件中定义。

3.标准库vector类型

vector是同一种类型的对象的集合,每个对象都有一个对应的整数索引值。和string对象一样,标准库将负责管理与存储元素相关的内存。我们把vector称为容器,是因为它可以包含其他对象。一个容器的所有对象都必须是同一种类型。使用vector之前,必须包含相应的头文件如下:

#include <vector>
using std::vector;
vector是一个类模板(class template),使用模板可以编写一个类定义或函数定义,可用于多个不同的数据类型。声明从类模板产生某种类型的对象,需要提供附加的信息,信息的种类取决于模板的定义。以vector为例,必须说明vector保存何种对象的类型,通过将类型放在类模板名称后面的尖括号中来指定类型:
vector<int> ivec; //ivec holds onjects of type int
vector<Sales_item> Sales_vec; //holds Sales_item
vector不是一种数据类型,而只是一个类模板,可用来定义任意多种数据类型。vector类型的每一种都指定了其保存元素的类型。因此vector<int>和vector<string>都是数据类型。vector定义了好几种构造函数,用来定义和初始化vector对象,如下表所示。

几种初始化vector对象的方式
vector<T>  v1;
vector保存类型为T的对象。默认构造函数,v1为空
vector<T>  v2(v1);
v2是v1的一个副本
vector<T>  v3(n,i);
v3包含n个值为i的元素
vector<T>  v4(n);
v4包含值初始化的元素n个副本

如果没有指定元素初始化式,那么标准库将自行提供一个元素初始值进行值初始化(value initializationed),至于如何初始化则取决于存储于vector中元素的数据类型。

1)如果是内置类型,那么标准库将用0来创建元素初始化式

2)如果是含有构造函数的类类型(如string),那么标准库将用该类型的默认构造函数创建元素初始化式

3)若元素类型是没有定义任何构造函数的类类型,那么标准库仍产生一个带初始值的对象,对象每个成员进行了值的初始化

vector标准库提供了许多类似于string类型的操作,下面列出了几种最重要的vector操作。

vector对象的操作
v.empty()
如果v为空,则返回true,否则返回fasle
v.size()
返回v中元素的个数
v.push_back(t)
在v的末尾增加一个值为t的元素
v[n]
返回v中位置为n的元素
v1 = v2
把v1中的元素替换为v2中元素的副本
v1 == v2
如果v1与v2相等,则返回true
!=,<,<=,>,>=
保持这些操作的惯有含义
vector中的对象是没有命名的,可以按vector中对象的位置来访问它们。通常使用下标操作符来获取元素,和string类型的下表操作符一样,vector下标操作符的结果为左值,且使用vector<T>::size_type类型作为vector的下标类型。需要注意的是vector对象的下标只能用于获取已存在的元素,通过下标操作进行赋值时,不会添加任何元素。

3.迭代器的简介

除了使用下标来访问vector对象的元素外,标准库还提供了另外一种访问元素的方法:迭代器(iterator)。迭代器是一种检查容器内元素并遍历元素的数据类型。标准库为每一种标准容器定义了一种迭代器类型,如vector:

vector<int>::iterator iter=ivec.begin();
每种容器都定义了一对命名为begin和end的函数,用于返回迭代器。如果容器中有元素的话,由begin返回的迭代器指向第一个元素,由end操作返回的迭代器指向vector的末端元素的下一个,通常称为超出末端迭代器(off-the-end iterator),表示它指向一个不存在的元素。如果容器为空,begin与end返回的迭代器相同。迭代器类型可使用解引用操纵符(*操作符)来访问迭代器所指向的元素,例如*iter;迭代器使用自增操作符向前移动迭代器指向容器的下一个元素。由于end操作返回的迭代器不指向任何元素,因此不能对它进行解引用或自增操作。使用==和!=操作符可以用来比较两个迭代器,如果两个迭代器对象指向容器的同一个元素,则它们相等,否则不相等。
//equivalent loop using iterators to reset all the elements in ivec to 0
for(vector<int>::iterator iter = ivec.begin(); iter != ivec.end(); ++iter){
    *iter = 0; //set element to which iter refers to 0
}
每种容器还定义了一种名为const_iterator的类型,该类型只能用于读取容器内的元素,但不能改变读取元素的值。当我们对普通的iterator类型解引用时,得到对某个元素的非const引用。而如果我们对const_iterator类型解引用时,则可以得到一个指向const对象的引用,同任何常量一样,该对象不能重写。不要把const_iterator对象和const的iterator对象混淆起来。
//an iterator that cannot write elements
vector<int>::const_iterator
//an iterator whose value cannot change
const vector<int>::iterator
除了一次移动迭代器的一个元素的增量操作符外,迭代器也支持其他的算术操作,这些操作成为迭代器的算术操作(iterator arthmetic),包括:

1)iter + n     或iter -n :n应该是size_type或者difference_type

2iter1 - iter2 :得到一个距离,名为difference_type的signed类型值,它类似于size_type类型,也是由容器自定义的

vector<int>::iterator mid = vi.begin() + vi.size() /2 ;// get middle position
4.标准库bitset类型

有些程序要处理二进制位的有序集,每个位包含0值或1值。标准库提供了bitset类简化了位集的处理。

#include <bitset>
using std::bitset;
类似于vector,bitset类也是一种类模板;而与vector不一样的是bitset类型对象的区别仅在于其长度而不在其类型。在定义bitset时,要明确bitset含有多少位,需在尖括号内给出它的长度值。
bitset<32> bitvec; // 32bits, all zero
位集合的位置编号从0开始,以0开始的位串是低阶位(low-order bit)。

初始化bitset对象的方法
bitset<n> b;
b有n位,每位都为0
bitset<n> b(u);
b是unsigned long型u的一个副本
bitset<n> b(s);
b是string 对象s中含有的位串的副本
bitset<n> b(s, pos, n);
b是s中从位置pos开始的n个位的副本

多种bitset操作用来测试或设置bitset对象中的单个或多个二进制位。通过bitset对象的操作,我们可以测试整个bitset对象、访问bitset对象中的位、对整个bitset对象进行设置以及获取bitset对象的值。bitset对象所支持的操作如下表所示:

bitset对象的操作
b.any()
b中存在值为1的二进制位,则返回true
b.none()
b中不存在值为1的二进制位(即全为0),则返回true
b.count()
b中值为1的二进制位个数
b.size()
b中二进制位个数
b[pos]
访问b中pos处的二进制位
b.test(pos)
b中pos处的二进制位为1,则返回true
b.set()
把b中所有的二进制为都置为1
b.set(pos)
把b中pos处的二进制位置为1
b.reset()
把b中所有的二进制位都置为0
b.reset(pos)
把b中pos处的二进制位置为0
b.flip()
把b中所有的二进制位逐位取反
b.flip(pos)
把b中pos处的二进制位取反
b.to_ulong()
用b中同样的二进制位返回一个unsigned long值
os << b
把b中的位集输出到os流

(完)

56.8K

发表评论:

© 云中布衣 2015 | Driven by EMLOG  | SiteMap | RunTime: 7.26ms&RSS  |   | 回到顶部

文章数量【230】 评论数量【158】 稳定运行【1089天】

Visitor IP Address【54.224.111.99】