| yc's profileyc1229PhotosBlogLists | Help |
|
July 19 第二章 变量和基本类型 2.1基本内置类型
C++标准规定了每个算术类型的最小存储空间,但它并不阻止编译器使用更大的存储空间。对于int类型,几乎所有的编译器使用的存储空间都比所要求的大。 这些类型所能表示的最大(最小)值也因机器的不同而有所不同。 wchar_t 宽字符型 16位 2.1.1整型
通常将8位的块作为一个字节,32位或4个字符作为一个"字"。(64位机器中64位为一个字) 一般short类型为半个机器字长,int类型为一个机器字长,而long类型为一个或两个机器字长(在32位机器中int类型和long类型通常字长是相同的)。 bool类型表示真值true和false。0值算术类型代表false,任何非0的值都代表true。 1.带符号和无符号类型 整型int、short和long都默认为带符号型,要获得无符号型则必须指定该类型为unsigned,unsigned int类型可以简写为unsigned。 2.整型值的表示 C++标准并未定义signed类型如何用位来表示,而是由每个编译器自由决定如何表示signed类型。这些表示方法会影响signed类型的取值范围。8位signed类型的取值肯定至少是从-127到127,但也有许多实现允许取值从-128到127。 3.整型的赋值 对于unsigned类型来说,编译器必须调整越界值使其满足要求。编译器会将该对unsigned类型的可能取值求模,然后取所得值。比如8位的unsigned char,其取值范围从0到255(包括255)。如果赋给超出这个范围的值,那么编译器将会取该值对256求模后的值。例如,如果试图将336存储到8位的unsigned char中,则实际赋值为80,因为80是336对256求模后的值。 C++中,把负值赋给unsigned对象是完全合法的,其结果是该负数对该类型的取值个数求模后的值。所以,如果把-1赋给8位的unsigned char,那么结果是255,因为255是-1对256求模后的值。 当将超过取值范围的值赋给signed类型时,由编译器决定实际赋的值,在实际操作中,很多的编译器处理signed类型的方式和unsigned类型类似。 2.1.2浮点型
对于实际的程序来说,float类型精度通常是不够的——float型只能保证6位有效数字,而double型至少可以保证10位有效数字,能满足大多数计算的需要。 当执行整型算术运算时,很少使用short类型,比较典型的情况是值“截断”以至于因越界而变成很大的负数,虽然char类型被当作signed类型,在另一些应用中则被当作unsigned类型,因此把char类型作为计算类型使用时容易出问题。 使用double类型基本上不会有错。在float类型中隐式的精度损失是不能忽视的,而double类型精度代价相对于float类型精度代价可以忽略。事实上,有些机器上,double类型比float类型的计算要快得多,long double类型提供的精度通常没有必要,而且还需要承担额外的运行代价。 2.2字面值常量
只有内置类型存在字面值,没有类类型的字面值。 1.整型字面值规则 20 //十进制 024 //八进制 0x14 //十六进制 以0开头的字面值整数常量表示八进制,以0x或0X开头的表示十六进制。 字面值整数常量的类型默认int或long类型。其精度类型决定于字面值——其值适合int就是int类型,比int大的值就是long类型。通过增加后缀,能够强制将字面值整数常量转换为long、unsigned或unsigned long类型。通过在数值后面加L或者l指定常量为long类型。 定义长整型时,应该使用大写字母L。小写字母l很容易和数值l混淆。 可通过在数值后面加U或u定义unsigned类型。 128u //unsigned 1024UL //unsigned long 1L //long 8Lu //unsigned long 没有short类型的字面值常量。 2.浮点字面值规则 通常可以用十进制或者科学计数法来表示浮点字面值常量。使用科学计数法时,指数用E或者e表示。默认的浮点字面值常量为double类型。在数值的后面加上F或f表示单精度。同样加上L或者l表示扩展精度。 3.布尔字面值和字符字面值 单词true和false是布尔型的字面值 在字符字面值上加L就能够得到wchar+t类型的宽字符字面值,如: L'a' 4.非打印字符的转义序列 我们可以将任何字符表示为以下形式的通用转义字符: \ooo 这里\ooo表示三个八进制数字,这三个数字表示字符的数字值。 同样也可以用十六进制转义字符来定义字符: \xddd 5.字符串字面值 为了兼容C语言,C++中所有的字符串字面值都由编译器自动在末尾加一个空字符(\0) 存在宽字符串字面值,一样也在前面加“L” L"a wide string literal" 宽字符串字面值一串常量字符,同样以一个宽空字符结束。 6.字符串字面值的连接 std::cout<<"a multi-line" "b " "c " <<endl; std::cout<<"multi=line" L"literal"<<std::endl;
其结果是未定义的,也就是说,连接不同类型的行为标准没有定义。 7.多行字面值 在一行的末尾加一反斜线符号可将此行和下一行当作同一行处理。(\) 注意反斜线符号必须是该行的尾字符——不允许有注释或空格符。同样,后继行行首的任何空格和制表符都是字符串字面值的一部分。正因如此,长字符串字面值的后继行才不会有正常的缩进。 通常程序不应该依赖机器相当的行为。 2.3.1什么是变量
(1)左值:左值可以出现在赋值语句的左边或右边 (2)右值:右值只能出现在赋值的右边,不能出现在赋值语句的左边。 2.3.2变量名 语言本身并没有限制变量名的长度,但考虑到将会阅读和/或修改我们的代码的其他人,变量名不应太长。 1.C++关键字 标识符不能包含两个连续的下划线,也不能以下划线开头后面紧跟一个大写字母。有些标识符(在函数外定义的标识符)不能以下划线开头。 2.3.3定义对象
1.初始化 定义时指定了初始值的对象被称为已初始化的。C++支持两种初始化变量的形式:复制初始化和直接初始化。复制初始化语法用等号,直接初始化则是把初始化式放在括号中: int ival(1024); //直接初始化 int ival=1024; //复制初始化 初始化指创建变量并给它赋初始值,而赋值则是擦除对象当前值并用新值代替。 2.使用多个初始化式 对人置类型来说,复制初始化和直接初始化几乎没有差别。 使用string时必须包含string头文件。string定义在std命名空间中。 2.3.4变量初始化规则
当定义没有初始化式的变量时,系统有时候会帮我们初始化变量。这时,系统提供什么样的值取决于变量的类型,也取决于变量定义的位置。 1.内置类型变量的初始化 在函数体外定义的变量都初始化成0。 建议每个内置类型的对象都要初始化,虽然这样并不总是必需的,但是会更加容易和安全,除非你确实忽略初始化式不会带来风险。 2.3.5声明和定义
变量的定义用于为变量分配存储空间,还可以为变量指定初始值。在一个程序中,变量有且仅有一个定义。 声明用于向程序表明变量的类型和名字。定义也是声明:当定义变量时我们声明了它的类型和名字。可以通过使用extern关键字声明变量名而不定义它。 extern int i; extern声明不是定义,由不分配存储空间。事实上,它只是说明变量定义在程序的其他地方。程序中变量可以声明多次,但只有定义一次。 只有当声明也是定义时,声明才可以有初始化式,因为只有定义才分配存储空间,如果声明有初始化式,那么它可以被当作是定义,即使声明标记为extern: extern double pi=3.1416; 虽然使用了extern,但是这条语句还是定义了pi,分配初始化了存储空间,只有当extern声明位于函数外部时,才可以含有初始化式。 2.4 const限定符 魔数:它的意义在上下文中没有体现出来,好像这个数是魔术般地从空中出现的。 1.定义const对象 因为常量在定义后就不能被修改,所以定义时必须初始化。 2.const对象默认为文件的局部变量 除非特别说明,在全局作用声明的const变量是定义该对象的文件的局部变量,此变量只存在于那个文件中,不能被其他文件访问。 通过const变量为extern,就可以在整个程序中访问const对象: //file_1.cc extern const int bufSize=fcn(); //file_2.cc extern const int bufSize; 非const变量默认为extern。要使const变量能够在其他的文件中访问,必须显式地指定它为extern。 2.5引用
3.const引用 const int ival=1024 const int &refVal=ival; //ok int &ref2=ival; //error const引用可以初始化不同类型的对象或者初始化为右值,如字面值常量: int i=42; const int &r=42; const int &r2=r+i; 非const引用只能绑定到与该引用同类的对象。const引用则可以绑定到不同但相关的类型的对象或绑定到右值。 2.7枚举
1.定义和初始化枚举 默认地,第一个枚举成员赋值为0,后面的每个枚举成员赋的值比前面的大1。 2.枚举成员是常量 enum Forms {shape=1,sphere,cylinder,polygon}; //shape is 1 //sphere is 2,cylinder is 3,polygon is 4 enum Points {point2d=2,point2w,point3d=3,point3w}; //point2d is 2,point2w is 3,point3d is 3,point3w is 4 3.每个enum都定义一种唯一的类型 每个enum都定义了一种新的类型。枚举类型的对象的初始化或赋值,只能通过期枚举成员或同一枚举类型的其他对象来进行: Points pt3d=point3d; //ok Points pt2w=3; //error pt2w=polygon; //error pt2w=pt3d; //ok 2.8类类型 1.从操作开始设计类 每个类都定义了一个接口和一个实现。接口由使用该类的代码需要执行的操作组成。实现一般包括该类所需要的数据。 定义类时,通常先定义该类的接口,通过这些操作,可以决定该类完成其功能所需要的数据,以及是否需要定义一些函数来支持该类的实现。 2.定义Sales_item类 操作和数据是类的一部分,也称为类的成员。操作称为成员函数,而数据则称为数据成员。 3.类的数据成员 一般不能把类成员的初始化作为其定义的一部分。当定义数据成员时,只能指定该数据成员的名字和类型。 4.访问标号 访问标号public、private可以多次出现在类定义中。给定的访问标号应用到下一个访问标号出现时为止。 5.使用struct关键字 如果使用class关键字来定义类,那么定义在第一个访问标号前的任何成员都隐式指定为private;如果使用struct关键字,那么这些成员都是public。使用class还是struct关键字来定义类,仅仅影响默认的初始访问级别。 可以等效地定义Sales_item类为: struct Sales_item{ private: std:string isbn; unsigned units_sold; double revenue; }; 用class和struct关键字定义类的唯一差别在于默认访问级别:默认情况下,struct的成员为public,而class的成员为private。 2.9编写自己的头文件 2.9.1设计自己的头文件 1.头文件用于声明而不是用于定义 下列语句是一些定义,所以不应该放在头文件里: extern int ival=10; double fica_rate; 虽然ival声明为extern,但是它有初始化式,代表这条语句是一个定义。类似地,fica_rate的声明虽然没有初始化,但也是一个定义,因为没有关键extern 因为头文件包含在多个源文件中,所以不应该含有变量或函数的定义。 对于头文件不应该含有定义这一规则,有三个例外。头文件可以定义类,值在编译时就已知道的const对象和inline函数 2.9.2预处理器的简单介始 1.头文件经常需要其他头文件 包含其他头文件是如此司空见惯,甚至一个头文件被多次包含进一个源文件也不稀奇。例如,使用Sales_item头文件的程序也可能使用string库。该程序不会(也不应该)知道Sales_item头文件使用了string库。在这种情况下,string头文件被包含了两次:一次是通过程序本身直接包含,别一次是通过包含Sales_item头文件而间接包含。 我们必须保证多次包含同一头文件不会引起该头文件定义的类和对象被多次定义。使得头文件安全的通用做法,是使用预处理器定义头文件保护符。头文件保护符用于避免在已经见到头文件的情况下重新处理该头文件的内容。 2.避免多重包含 #ifndef指示检测指定的预处理器变量是否未定义。如果预处理器变量未定义,那么跟在其后面的所有指示都被处理,直到出现#endif。 头文件应该含有保护符,即使这些头文件还会被其他头文件包含。编写头文件保护符并不困难,而且如果头文件被包含多次,它可以避免难以理解的编译错误。 TrackbacksThe trackback URL for this entry is: http://yc1229.spaces.live.com/blog/cns!C863EF037BDFF0CE!108.trak Weblogs that reference this entry
|
|
|