Archive for 15 4 月, 2010

C++ Primer Chapter3

星期四, 15 4 月, 2010

1、使用using声明可以在不需要加前缀namespace_name::的情况下访问命名空间中的名字。using声明的形式如下:

using namespace::name;    // 如using std::cout;

有一种情况下,必须总是使用完全限定的标准库名字:在头文件中。理由是头文件的内容会被预处理器复制到程序中。用#include包含文件时,相当于头文件中的文本将成为我们编写的文件的一部分。如果在头文件中放置了using声明,就相当于在包含该头文件的每个程序中都放置了同一using声明,不论该程序是否需要using声明。

通常,头文件中应该只定义确实必要的东西。请养成这个好习惯。

2、string IO操作getline接受两个参数:一个输入流对象和一个string对象。

while(getline(cin, line))

string类类型和许多其他库类型都定义了一些配套类型。通过这些配套类型,库类型的使用就能与机器无关。size_type就是这些配套类型中的一种。它定义为与unsigned型(unsigned int或unsigned long)具有相同的含义,而且可以保证足够大能够存储任意string对象的长度。为了使用由string类型定义的size_type类型,程序员必须加上作用域操作符来说明所使用的size_type类型是由string类定义的。

任何存储string的size操作结果的变量必须为string::size_type类型。特别重要的是,不要把size的返回值赋给一个int变量。string自己负责size_type一定能容纳的最大长度,且unsigned型所能表示的最大正数值比对应的signed型要大一倍。

3、C标准库头文件命名形式为name.h,而C++版本则命名为cname,少了后缀.h而在头文件名前加了c。c表示这个头文件源自C标准库。因此,cctype与ctype.h文件的内容是一样的,只是采用了更适合C++程序的形式。特别地,cname头文件中定义的名字都定义在命名空间std内,而.h版本中的名字却不是这样。

4、vector不是一种数据类型,而只是一个类模板,可用来定义任意多种数据类型。vector类型的每一种都指定了其保存元素的类型。因此vector<int>和vector<string>都是数据类型。

vector中的对象是没有命名的。就像string中的字符对象、数组中的对象。

必须是已存在的元素才能用下标操作符([])进行索引。通过下标操作进行赋值时,不会添加任何元素。

由end操作返回的迭代器指向vector的“末端元素的下一个”。通常成为超出末端迭代器,表明它指向了一个不存在的元素。如果vector为空,begin返回的迭代器与end返回的迭代器相同。

每种容器类型还定义了一种名为const_iterator的类型,该类型只能用于读取容器内元素,但不能改变其值。使用const_iterator类型时,我们可以得到一个迭代器,它自身的值可以改变,但不能用来改变其所指向的元素的值。

不要把const_iterator对象与const的iterator对象混淆起来。声明一个const迭代器时,必须初始化迭代器。一旦被初始化后,就不能改变它的值。

5、类似于vector,bitset类是一种类模板;而与vector不一样的是bitset类型对象的区别仅在其长度而不在其类型。在定义bitset时,要明确bitset含有多少位,须在尖括号内给出它的长度值:

bitset<32> bitvec;

C++ Primer Chapter2

星期四, 15 4 月, 2010

1、C++定义了一组表示整数、浮点数、单个字符和布尔值的算术类型,另外还定义了一种称为void的特殊类型。

算术类型的存储空间依机器而定。C++标准规定了每个算术类型的最小存储空间。

通常将8位的块作为一个字节,32位或4个字节作为一个“字(word)”。一般short类型为半个机器字(word)长,int类型为一个机器字长,而long类型为一个或两个机器字长(在32位机器中int类型和long类型通常字长是相同的)。

表示整数、字符和布尔值的算术类型合称为整形。

2、对于unsigned类型来说,编译器必须调整越界值使其满足要求。编译器会将该值对unsigned类型的可能取值数目求模,然后取所得值。

当将超过取值范围的值赋给signed类型时,由编译器决定实际赋的值。在实际操作中,很多的编译器处理signed类型的方式和unsigned类型类似。

3、字面值常量。称之为字面值是因为只能用它的值称呼它,称之为常量是因为它的值不能修改。只有内置类型存在字面值,没有类类型的字面值。

字面值整数常量的类型默认为int或long类型(没有short类型的字面值常量)。

默认的浮点字面值常量为double类型。

为兼容C语言,C++中所有的字符串字面值都由编译器自动在末尾添加一个空字符。

两个相邻的仅由空格、制表符或换行符分开的字符串字面值(或宽字符串字面值),可连接成一个新字符串字面值。

std::cout << “a multi-line ”
“string literal ”
“using concatenation”
<< std::endl;

4、C++是一门静态类型语言,在编译时会作类型检查。

变量提供了程序可以操作的有名字的存储区。

除了关键字,C++标准还保留了一组标识符用于标准库。标识符不能包含两个连续的下划线(__id),也不能以下划线开头后面紧跟一个大写字母(_Id)。有些标识符(在函数外定义的标识符)不能以下划线开头(_)。

5、C++支持两种初始化变量的形式:复制初始化和直接初始化。复制初始化语法用等号,直接初始化则是把初始化式放在括号中:

int ival = 1024   // 复制初始化

int ival(1024);    // 直接初始化

对内置类型来说,复制初始化和直接初始化几乎没有差别。而对类类型对象,直接初始化语法更灵活且效率更高。

在C++中理解“初始化不是赋值”是必要的。初始化指创建变量并给它赋初始值,而赋值则是擦除对象的当前值并用新值代替。

6、内置类型变量是否自动初始化取决于变量定义的位置。在函数体外定义的变量都初始化成0,在函数体里定义的内置类型变量不进行自动初始化。

7、变量的定义用于为变量分配存储空间,还可以为变量指定初始值。在一个程序中,变量有且仅有一个定义。

声明用于向程序表明变量的类型和名字。定义也是声明:当定义变量时我们声明了它的类型和名字。可以通过使用extern关键字声明变量名而不定义它。

如果声明有初始化式,那么它可被当作是定义,及时声明标记为extern。(只有当extern声明位于函数外部时,才可以含有初始化式。)

8、const:因为常量在定义后就不能被修改,所以定义时必须初始化。参考资料:CONST

非const变量默认为extern。要使const变量能够在其他的文件中访问,必须显示地指定它为extern。(只有变量在声明定义时指定了extern,才能在其他文件中使用extern声明该变量后直接使用。)

9、引用就是对象的另一个名字。

引用是一种复合类型。复合类型是指用其他类型定义的类型。在引用的情况下,每一种引用类型都“关联到”某一其他类型。不能定义引用类型的引用,但可以定义任何其他类型的引用。

当引用初始化后,只要该引用存在,它就保持绑定到初始化时指定的对象。不可能将引用绑定到另一个对象。

const引用是指向const对象的引用。即引用对象不能修改。

非const引用只能绑定到与该引用同类型的对象。const引用则可以绑定到不同但相关的类型的对象或绑定到右值。

10、常量表达式是编译器在编译时就能够计算出结果的整型表达式。

枚举成员值可以是不唯一的。

11、用class和struct关键字定义类的唯一差别在于默认访问级别:默认情况下,struct的成员为public,而class的成员为private。

12、因为头文件包含在多个源文件中,所以不应该含有变量或函数的定义。有三个例外:头文件可以定义类、值在编译时就已知道的const对象和inline函数。

回想一下,const变量默认时是定义该变量的文件的局部变量。正如我们现在所看到的,这样设置默认情况的原因在于允许const变量定义在头文件中。

当该const变量是用常量表达式初始化时,可以保证所有的变量都有相同的值。但是在实践中,大部分的编译器在编译时都会用相应的常量表达式来替换对这些const变量的使用。所以,在实践中不会有任何存储空间用于存储常量表达式初始化的const变量。参考资料:const

13、预处理器用指定的头文件的内容替代每个#include。

如果头文件名括在尖括号(<>)里,那么认为该头文件是标准头文件。编译器将会在预定义的位置集查找该头文件。

14、所谓魔数和魔字符串是指在代码中出现但没有解释的数字常量或字符串常量。