1.string类型和字符串字面值:
书上有一句话,很重要:
因为历史原因以及为了与 C 语言兼容,字符串字面值与标准库 string 类型不是同一种类型。这一点很容易引起混乱,编程时一定要注意区分字符串字面值和 string 数据类型的使用,这很重要。
字符串字面值的类型是字符常量的数组,现在可以更明确地认识到:字符串字面值的类型就是const char类型的数组.C++从C语言继承下来的一种通用结构是C风格字符串(C-style character string),而字符串字面值就是该类型的实例.
关于字符串字面值,一向对这个概念很模糊,好不容易搜到一篇资料加深理解:http://learn.akae.cn/media/ch08s04.html
2.string的输出输入:
从标准输入读取 string 并将读入的串存储在 s 中。string 类型的输入操作符:
Reads and discards any leading whitespace (e.g., spaces, newlines, tabs)
读取并忽略开头所有的空白字符(如空格,换行符,制表符)。
It then reads characters until the next whitespace character is encountered
读取字符直至再次遇到空白字符,读取终止。
注意:如果给定和上一个程序同样的输入,则输出的结果是"Hello World!"(注意到开头和结尾的空格),则屏幕上将输出"Hello",而不含任何空格。
(问题:while (cin >> word)
cout << word << endl;
string型遇到空白字符都会终止。但是上面这个循环,我输入空格的时候不会输出,输入换行符才会执行输出语句,这是为什么呢?
答:缓冲方式的输入,是系统来处理的,直到发生回车这样的输入或者缓冲区满,比如,当你输入个abcde fghij klmn只要你还没按回车,那么,实际上,你的程序没有接收到任何输入,这些输入都在系统缓冲区呢,与你的程序无关 。
)
这里补充关于缓冲区的知识,网上搜到的:http://www.examda.com/ncre2/cpp/jichu/20100815/082041550.html
原文如下:
什么是缓冲区
缓冲区根据其对应的是输入设备还是输出设备,分为输入缓冲区和输出缓冲区。
为什么要引入缓冲区
我们为什么要引入缓冲区呢?
比如我们从磁盘里取信息,我们先把读出的数据放在缓冲区,计算机再直接从缓冲区中取数据,等缓冲区的数据取完后再去磁盘中读取,这样就可以减少磁盘的读写次数,再加上计算机对缓冲区的操作大大快于对磁盘的操作,故应用缓冲区可大大提高计算机的运行速度。
又比如,我们使用打印机打印文档,由于打印机的打印速度相对较慢,我们先把文档输出到打印机相应的缓冲区,打印机再自行逐步打印,这时我们的CPU可以处理别的事情。
现在您基本明白了吧,缓冲区就是一块内存区,它用在输入输出设备和CPU之间,用来缓存数据。它使得低速的输入输出设备和高速的CPU能够协调工作,避免低速的输入输出设备占用CPU,解放出CPU,使其能够高效率工作。
缓冲区的类型
缓冲区 分为三种类型:全缓冲、行缓冲和不带缓冲。
1、全缓冲
在这种情况下,当填满标准I/O缓存后才进行实际I/O操作。全缓冲的典型代表是对磁盘文件的读写。
2、行缓冲
在这种情况下,当在输入和输出中遇到换行符时,执行真正的I/O操作。这时,我们输入的字符先存放在缓冲区,等按下回车键换行时才进行实际的I/O操作。典型代表是键盘输入数据。
3、不带缓冲
也就是不进行缓冲,标准出错情况stderr是典型代表,这使得出错信息可以直接尽快地显示出来。
缓冲区的刷新
下列情况会引发缓冲区的刷新:
1、缓冲区满时;
2、执行flush语句;
3、执行endl语句;
4、关闭文件。
可见,缓冲区满或关闭文件时都会刷新缓冲区,进行真正的I/O操作。另外,在C++中,我们可以使用flush函数来刷新缓冲区(执行I/O操作并清空缓冲区),如:
cout<<flush; //将显存的内容立即输出到显示器上进行显示
endl控制符的作用是将光标移动到输出设备中下一行开头处,并且清空缓冲区。
cout<<endl;
相当于
cout<<”/n” <<flush;
通过实例演示说明
1、文件操作演示全缓冲
创建一个控制台工程,输入如下代码:
#include <fstream>
using namespace std;
int main()
{
//创建文件test.txt并打开
ofstream outfile("test.txt");
//向test.txt文件中写入4096个字符’a’
for(int n=0;n<4096;n++)
{
outfile<<’a’;
}
//暂停,按任意键继续
system("PAUSE");
//继续向test.txt文件中写入字符’b’,也就是说,第4097个字符是’b’
outfile<<’b’;
//暂停,按任意键继续
system("PAUSE");
return 0;
}
3.getline函数:
getline函数接受两个参数:一个输入流对象和一个 string 对象。getline 函数从输入流的下一行读取,并保存读取的内容到不包括换行符。和输入操作符不一样的是,getline 并不忽略行开头的换行符。只要 getline 遇到换行符,即便它是输入的第一个字符,getline 也将停止读入并返回。getline 函数将 istream 参数作为返回值
int main()
{
string line;
// read line at time until end-of-file
while (getline(cin, line))
cout << line << endl;
return 0;
}
4.string::size_type型:
书上描述到:
string 类类型和许多其他库类型都定义了一些配套类型(companion type)。通过这些配套类型,库类型的使用就能与机器无关(machine-independent)。size_type 就是这些配套类型中的一种。它定义为与 unsigned 型(unsigned int 或 unsigned long)具有相同的含义,而且可以保证足够大能够存储任意 string 对象的长度。为了使用由 string 类型定义的 size_type 类型是由 string 类定义。
这句话怎么解释呢?看看神奇的百度知道上的答案吧:
size_type是为string类类型和vector类类型定义的类型,用以保存任意string对象或vector对象的长度,标准库类型将size_type定义为unsigned类型 string抽象意义是字符串, size()的抽象意义是字符串的尺寸, string::size_type抽象意义是尺寸单位类型 string::size_type它在不同的机器上,长度是可以不同的,并非固定的长度。但只要你使用了这个类型,就使得你的程序适合这个机器。与实际机器匹配。 eg string::size_type从本质上来说,是一个整型数。关键是由于机器的环境,它的长度有可能不同。 例如:我们在使用 string::find的函数的时候,它返回的类型就是 string::size_type类型。而当find找不到所要找的字符的时候,它返回的是 npos的值,这个值是与size_type相关的。假如,你是用 string s; int rc = s.find(.....); 然后判断,if ( rc == string::npos ) 这样在不同的机器平台上表现就不一样了。如果,你的平台的string::size_type的长度正好和int相匹配,那么这个判断会侥幸正确。但换成另外的平台,有可能 string::size_type的类型是64位长度的,那么判断就完全不正确了。 所以,正确的应该是: string::size_type rc = s.find(.....); 这个时候使用 if ( rc == string::npos )就回正确了。
这样就清楚了吧
为什么要使用size_type型呢?书上已经做出了解释了:
虽然我们不知道 string::size_type 的确切类型,但可以知道它是 unsigned 型。对于任意一种给定的数据类型,它的 unsigned 型所能表示的最大正数值比对应的 signed 型要大倍。这个事实表明 size_type 存储的 string 长度是 int 所能存储的两倍。
使用 int 变量的另一个问题是,有些机器上 int 变量的表示范围太小,甚至无法存储实际并不长的 string 对象。如在有 16 位 int 型的机器上,int 类型变量最大只能表示 32767 个字符的 string 个字符的 string 对象。而能容纳一个文件内容的 string 对象轻易就会超过这个数字。因此,为了避免溢出,保存一个 stirng 对象 size 的最安全的方法就是使用标准库类型 string::size_type。
5.string和字符串字面值的连接:
当进行 string 对象和字符串字面值混合连接操作时,+ 操作符的左右操作数必须至少有一个是 string 类型的:
string s5 = s1 + ", " + "world"; // ok: each + has string operand
string s6 = "hello" + ", " + s2; // error: can't add string literals
6.下标操作:
string 类型通过下标操作符([ ])来访问 string 对象中的单个字符。下标操作符需要取一个 size_type 类型的值,来标明要访问字符的位置。这个下标中的值通常被称为“下标”或“索引”。
下表操作可用作左值。
任何可产生整型值的表达式可用作下标操作符的索引。如:
str[someotherval * someval] = someval;(someotherval和someval为整型)
虽然任何整型数值都可作为索引,但索引的实际数据类型却是类型 unsigned 类型 string::size_type。
注意:使用 size_type 类型时,必须指出该类型是在哪里定义的。如:
vector<int>::size_type
7.string 对象中字符的处理:
适用于 string 对象的字符(或其他任何 char 值)。这些函数都在 cctype 头文件中定义。
8.关于vector:
vector的初始化:
如果 vector 保存内置类型(如 int 类型)的元素,那么标准库将用 0 值创建元素初始化式;
如果 vector 保存的是含有构造函数的类类型(如 string)的元素,标准库将用该类型的默认构造函数创建元素初始化式。
vector的下标操作 :
注意:vector的下标操作不添加元素。如:
vector<int> ivec; // 空的vector
for (vector<int>::size_type ix = 0; ix != 10; ++ix)
ivec[ix] = ix; // 错误: ivec没有元素
重点注意的一点是:
仅能对确知已存在的元素进行下标操作
试图获取不存在的元素必须产生运行时错误。和大多数同类错误一样,不能确保执行过程可以捕捉到这类错误,运行程序的结果是不确定的。由于取不存在的元素的结果标准没有定义,因而不同的编译器实现会导致不同的结果,但程序运行时几乎肯定会以某种有趣的方式失败。如:
vector<int> ivec; // empty vector
cout << ivec[0]; // Error: ivec has no elements!
vector<int> ivec2(10); // vector with 10 elements
cout << ivec[10]; // Error: ivec has elements 0...9
9.const_iterator和const的iterator的区别:
使用 const_iterator 类型时,我们可以得到一个迭代器,它自身的值可以改变,但不能用来改变其所指向的元素的值。可以对迭代器进行自增以及使用解引用操作符来读取值,但不能对该元素赋值。
vector<int> nums(10); // nums is nonconst
vector<int>::const_iterator cit = nums.begin();
*cit = 1; // 错误,不能改变其指向的值
++cit; // 正确,迭代器自身的值可以改变
而声明一个 const 迭代器时,必须初始化迭代器。一旦被初始化后,就不能改变它的值。
vector<int> nums(10); // nums is nonconst
const vector<int>::iterator cit = nums.begin();
*cit = 1; // 正确,可以改变其指向的值
++cit; // 错误,迭代器自身的值不能改变
10.迭代器支持的算术操作:
iter+n/iter-n
加上或减去的值的类型应该是 vector 的 size_type 或 difference_type 类型
iter1-iter2
该表达式用来计算两个迭代器对象的距离,该距离是名为 difference_type 的 signed 类型 size_type 的值,这里的 difference_type 是 signed 类型,因为减法运算可能产生负数的结果。该类型可以保证足够大以存储任何两个迭代器对象间的距离。iter1 与 iter2 两者必须都指向同一 vector 中的元素,或者指向 vector 末端之后的下一个元素。
迭代器不支持两个迭代器相加的算术操作:
iter1+iter2 //error
使用迭代器要注意的问题是:
任何改变 vector 长度的操作都会使已存在的迭代器失效。例如,在调用 push_back 之后,就不能再信赖指向 vector 的迭代器的值了。
11.bitset
对于bitset概念不是很熟,抄下书上的概念总结如下:
类似于 vector,bitset 类是一种类模板;而与 vector 不一样的是 bitset 类型对象的区别仅在其长度而不在其类型。在定义 bitset 时,要明确 bitset 含有多少位,须在尖括号内给出它的长度值:
bitset<32> bitvec; // 32 bits, all zero
当用 unsigned long 值作为 bitset 对象的初始值时,该值将转化为二进制的位模式。而 bitset 对象中的位集作为这种位模式的副本。如果 bitset 类型长度大于 unsigned long 值的二进制位数,则其余的高阶位将置为 0;如果 bitset 类型长度小于 unsigned long 值的二进制位数,则只使用 unsigned 值中的低阶位,超过 bistset 类型长度的高阶位将被丢弃。
当用 string 对象初始化 bitset 对象时,string 对象直接表示为位模式。从 string 对象读入位集的顺序是从右向左(from right to left):
string strval("1100");
bitset<32> bitvec4(strval);
bitvec4 的位模式中第 2 和 3 的位置为 1,其余位置都为 0。如果 string 对象的字符个数小于 bitset 类型的长度,则高阶位置为 0。
注意:
string 对象和 bitsets 对象之间是反向转化的:string 对象的最右边字符(即下标最大的那个字符)用来初始化 bitset 对象的低阶位(即下标为 0 的位)。当用 string 对象初始化 bitset 对象时,记住这一差别很重要。
与 vector 和 string 中的 size 操作一样,bitset 的 size 操作返回 bitset 对象中二进制位的个数,返回值的类型是 size_t::。size_t 类型定义在 cstddef 头文件中,该文件是 C 标准库的头文件 stddef.h 的 C++ 版本。它是一个与机器相关的 unsigned 类型,其大小足以保证存储内在中对象的大小。