C++ 入门笔记1
一.关于注释
注释总是以 /* 开始并以 */ 结束。这意味着,一个注释对不能出现在另一个注释对中。由注释对嵌套导致的编译器错误信息容易使人迷惑。
当注释掉程序的一大部分时,似乎最简单的办法就是在要临时忽略的区域前后放一个注释对。问题是如果那段代码已经有了注释对,那么新插入的注释对将提前终止。临时忽略一段代码更好的方法,是用编辑器在要忽略的每一行代码前面插入单行注释。这样,你就无需担心要注释的代码是否已包含注释对。
二.关于头文件
标准库的头文件用尖括号 < > 括起来,非标准库的头文件用双引号 " " 括起来。
三.数据类型
表示数值即整数和浮点数的类型。浮点型值有三种类型:long double 、double 和 float,分别表示扩展精度值、双精度值和单精度值。一般总是使用 double 型。特别地,float 只能保证六位有效数字,这对于大多数的计算来说都不够。
表示整数、字符和布尔值的算术类型合称为整型。整型包括 bool、char、wchar_t、short 、int 和 long 字符类型有两种:char 和 wchar_t。char 类型保证了有足够的空间,能够存储机器基本字符集中任何字符相应的数值,因此,char 类型通常是单个机器字节(byte)。wchar_t 类型用于扩展字符集,比如汉字和日语,这些字符集中的一些字符不能用单个 char 表示。 bool 类型表示真值 true 和 false。可以将算术类型的任何值赋给 bool 对象。0 值算术类型代表 false,任何非 0 的值都代表 true。
整型可以是带符号或无符号的。
1.除 bool 类型外,整型可以是带符号的(signed)也可以是无符号的(unsigned)。
2.顾名思义,带符号类型可以表示正数也可以表示负数(包括 0),而无符号型只能表示大于或等于 0 的数。 整型 int、short 和 long 都默认为带符号型。
3.要获得无符号型则必须指定该类型为 unsigned,比如 unsigned long。unsigned int 类型可以简写为 unsigned,也就是说,unsigned 后不加其他类型说明符意味着是 unsigned int 。
4.和其他整型不同,char 有三种不同的类型:plain char 、unsigned char 和 signed char。虽然 char 有三种不同的类型,但只有两种表示方式。可以使用 unsigned char 或 signed char 表示 char 类型。使用哪种 char 表示方式由编译器而定。
四.字面值
1.以 0(零)开头的字面值整数常量表示八进制,以 0x 或 0X 开头的表示十六进制. 没有 short 类型的字面值常量。
2.字面值整数常量的类型默认为 int 或 long 类型。其精度类型决定于字面值——其值适合 int 就是 int 类型,比 int 大的值就是 long 类型。通过增加后缀,能够强制将字面值整数常量转换为 long、unsigned 或 unsigned long 类型。通过在数值后面加 L 或者 l(字母“l”大写或小写)指定常量为 long 类型。
3.类似地,可通过在数值后面加 U 或 u 定义 unsigned 类型。同时加 L 和 U 就能够得到 unsigned long 类型的字面值常量。但其后缀不能有空格:
128u /* unsigned */ 1024UL /* unsigned long */ 1L /* long */ 8Lu /* unsigned long */没有 short 类型的字面值常量。.4.通常可以用十进制或者科学计数法来表示浮点字面值常量。使用科学计数法时,指数用 E 或者 e 表示。默认的浮点字面值常量为 double 类型。在数值的后面加上 F 或 f 表示单精度。同样加上 L 或者 l 表示扩展精度(再次提醒,不提倡使用小写字母l)。下面每一组字面值表示相同的值:3.14159F .001f 12.345L 0. 3.14159E0f 1E-3F 1.2345E1L 0e05.单词 true 和 false 是布尔型的字面值:bool test = false;可打印的字符型字面值通常用一对单引号来定义:
'a' '2' ',' ' ' // blank这些字面值都是 char 类型的。在字符字面值前加 L 就能够得到 wchar_t 类型的宽字符字面值。如:
L'a' 6.为了兼容 C 语言,C++ 中所有的字符串字面值都由编译器自动在末尾添加一个空字符。字符字面值'A' // single quote: character literal表示单个字符 A,然而
"A" // double quote: character string literal表示包含字母 A 和空字符两个字符的字符串。
7.处理长字符串有一个更基本的(但不常使用)方法,这个方法依赖于很少使用的程序格式化特性:在一行的末尾加一反斜线符号可将此行和下一行当作同一行处理。
// ok: A \ before a newline ignores the line break std::cou\ t << "Hi" << st\ d::endl;等价于std::cout << "Hi" << std::endl;注意反斜线符号必须是该行的尾字符——不允许有注释或空格符。同样,后继行行首的任何空格和制表符都是字符串字面值的一部分。正因如此,长字符串字面值的后继行才不会有正常的缩进。
8.两个相邻的仅由空格、制表符或换行符分开的字符串字面值(或宽字符串字面值),可连接成一个新字符串字面值。这使得多行书写长字符串字面值变得简单:
// concatenated long string literal std::cout << "a multi-line " "string literal " "using concatenation" << std::endl;执行这条语句将会输出:
a multi-line string literal using concatenation
五.变量
1.C++中的变量名,即变量的标识符,可以由字母、数字和下划线组成。变量名必须以字母或下划线开头,并且区分大小写字母:C++ 中的标识符都是大小写敏感的。
2.C++ 支持两种初始化变量的形式:复制初始化和直接初始化。复制初始化语法用等号(=),直接初始化则是把初始化式放在括号中:
int ival(1024); // direct-initialization int ival = 1024; // copy-initializationC++ 是一种静态类型语言:变量和函数在使用前必须先声明。变量可以声明多次但是只能定义一次。定义变量时就进行初始化几乎总是个好主意。
3.当一个定义中定义了两个以上变量的时候,每个变量都可能有自己的初始化式。 对象的名字立即变成可见,所以可以用同一个定义中前面已定义变量的值初始化后面的变量。已初始化变量和未初始化变量可以在同一个定义中定义。两种形式的初始化文法可以相互混合。
对象可以用任意复杂的表达式(包括函数的返回值)来初始化
4.定义时指定了初始值的对象被称为是已初始化的。当定义没有初始化式的变量时,系统有时候会帮我们初始化变量。这时,系统提供什么样的值取决于变量的类型,也取决于变量定义的位置。
内置类型变量是否自动初始化取决于变量定义的位置。在函数体外定义的变量都初始化成 0,在函数体里定义的内置类型变量不进行自动初始化。除了用作赋值操作符的左操作数,未初始化变量用作任何其他用途都是没有定义的。未初始化变量引起的错误难于发现。
5.C++中的对象:我们遵循更为通用的用法,即对象是内存中具有类型的区域。我们可以自由地使用对象描述程序中可操作的大部分数据,而不管这些数据是内置类型还是类类型,是有名字的还是没名字的,是可读的还是可写的。
6.类类型变量的初始化:每个类都定义了该类型的对象可以怎样初始化。类通过定义一个或多个构造函数来控制类对象的初始化
7.声明和定义:
变量的定义用于为变量分配存储空间,还可以为变量指定初始值。在一个程序中,变量有且仅有一个定义。
声明用于向程序表明变量的类型和名字。表明在程序中其他地方定义的变量、函数或类型的存在性。有些声明也是定义。只有定义才为变量分配存储空间。
定义也是声明:当定义变量时我们声明了它的类型和名字。可以通过使用extern关键字声明变量名而不定义它。不定义变量的声明包括对象名、对象类型和对象类型前的关键字extern:
extern int i; // declares but does not define i int i; // declares and defines iextern 声明不是定义,也不分配存储空间。事实上,它只是说明变量定义在程序的其他地方。程序中变量可以声明多次,但只能定义一次。
如果声明有初始化,则可以看作是定义:
extern double pi = 3.1416只有当 extern 声明位于函数外部时,才可以含有初始化式。
非 const 变量默认为 extern。
8.名字的作用域:用来区分名字的不同意义的上下文称为作用域. C++ 语言中,大多数作用域是用花括号来界定的。一般来说,名字从其声明点开始直到其声明所在的作用域结束处都是可见的。
#includeint main(){ int sum = 0; // sum values from 1 up to 10 inclusive for (int val = 1; val <= 10; ++val) sum += val; // equivalent to sum = sum + val std::cout << "Sum of 1 to 10 inclusive is " << sum << std::endl; return 0;}定义在所有函数外部的名字具有全局作用域.
变量 sum 有局部作用域
名字 val 更有意思,它定义在 for 语句的作用域中,只能在 for 语句中使用,而不能用在 main 函数的其他地方。它具有语句作用域。
9.一般来说,变量的定义或声明可以放在程序中能摆放语句的任何位置。变量在使用前必须先声明或定义。
通常把一个对象定义在它首次使用的地方是一个很好的办法。
10.const 限定符:因为常量在定义后就不能被修改,所以定义时必须初始化
与其他变量不同,除非特别说明,在全局作用域声明的 const 变量是定义该对象的文件的局部变量。此变量只存在于那个文件中,不能被其他文件访问。
通过指定 const 变更为 extern,就可以在整个程序中访问 const 对象:
// file_1.cc// defines and initializes a const that is accessible to other filesextern const int bufSize = fcn();// file_2.ccextern const int bufSize; // uses bufSize from file_1 // uses bufSize defined in file_1 for (int index = 0; index != bufSize; ++index) // ...11.引用是一种复合类型,通过在变量名前添加“&”符号来定义。复合类型是指用其他类型定义的类型。在引用的情况下,每一种引用类型都“关联到”某一其他类型。
复合类型是指用其他类型定义的类型,
a.不能定义引用类型的引用,但可以定义任何其他类型的引用。引用必须用与该引用同类型的对象初始化.
b.因为引用只是它绑定的对象的另一名字,作用在引用上的所有操作事实上都是作用在该引用绑定的对象上.
c.当引用初始化后,只要该引用存在,它就保持绑定到初始化时指向的对象。不可能将引用绑定到另一个对象。
引用的基本知识
引用就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。引用的声明方法:类型标识符 &引用名=目标变量名; 说明:
(1)&在此不是求地址运算,而是起标识作用。
(2)类型标识符是指目标变量的类型。
(3)声明引用时,必须同时对其进行初始化。
(4)引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,且不能再把该引用名作为其他变量名的别名。
int a,&ra=a;a为目标原名称,ra为目标引用名。给ra赋值:ra=1; 等价于 a=1;
(5)声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。故:对引用求地址,就是对目标变量求地址。&ra与a相等。
(6)不能建立数组的引用。因为数组是一个由若干个元素所组成的集合,所以无法建立一个数组的别名。
例如: Point pt1(10,10);
Point &pt2=pt1; 定义了pt2为pt1的引用。通过这样的定义,pt1和pt2表示同一对象。
需要特别强调的是引用并不产生对象的副本,仅仅是对象的同义词。因此,当下面的语句执行后:
pt1.offset(12,12);
pt1和pt2都具有(12,12)的值。
引用必须在定义时马上被初始化,因为它必须是某个东西的同义词。你不能先定义一个引用后才
初始化它。例如下面语句是非法的:
Point &pt3;
pt3=pt1;
12.const 引用可以绑定到 const 对象、非 const 对象或右值的引用。const 引用不能改变与其相关联的对象。const 引用可以初始化为不同类型的对象或者初始化为右值,如字面值常量:
int i = 42;// legal for const references only const int &r = 42; double dval = 3.14; const int &ri = dval;非 const 引用只能绑定到与该引用同类型的对象。
13.typedef 可以用来定义类型的同义词:
typedef double wages; // wages is a synonym for double typedef int exam_score; // exam_score is a synonym for int typedef wages salary; // indirect synonym for doubletypedef 定义以关键字 typedef 开始,后面是数据类型和标识符。标识符或类型名并没有引入新的类型,而只是现有数据类型的同义词。typedef 名字可出现在程序中类型名可出现的任何位置。
14.enum:
a.枚举的定义包括关键字 enum,其后是一个可选的枚举类型名,和一个用花括号括起来、用逗号分开的枚举成员列表。
b.可以为一个或多个枚举成员提供初始值,用来初始化枚举成员的值必须是一个常量表达式。常量表达式是编译器在编译时就能够计算出结果的整型表达式。整型字面值常量是常量表达式
c.不能改变枚举成员的值。枚举成员本身就是一个常量表达式,所以也可用于需要常量表达式的任何地方。
15.类类型:类定义以关键字 class 开始,其后是该类的名字标识符。类体位于花括号里面。花括号后面必须要跟一个分号。
a.定义了类,也就定义了一种新的类型。类名就是该类型的名字。通过命名 Sales_item 类,表示 Sales_item 是一种新的类型,而且程序也可以定义该类型的变量。
b.每一个类都定义了它自己的作用域.也就是说,数据和操作的名字在类的内部必须唯一,但可以重用定义在类外的名字。c.定义变量和定义数据成员存在非常重要的区别:一般不能把类成员的初始化作为其定义的一部分。当定义数据成员时,只能指定该数据成员的名字和类型。类不是在类定义里定义数据成员时初始化数据成员,而是通过称为构造函数的特殊成员函数控制初始化。d.用 class 和 struct 关键字定义类的唯一差别在于默认访问级别:默认情况下,struct 的成员为 public,而 class 的成员为 private。
16.头文件:头文件一般包含类的定义、extern 变量的声明和函数的声明。使用或定义这些实体的文件要包含适当的头文件。a.头文件的正确使用能够带来两个好处:保证所有文件使用给定实体的同一声明;当声明需要修改时,只有头文件需要更新。b.设计头文件还需要注意以下几点:头文件中的声明在逻辑上应该是统一的。编译头文件需要一定的时间。如果头文件太大,程序员可能不愿意承受包含该头文件所带来的编译时代价。为了减少处理头文件的编译时间,有些 C++ 的实现支持预编译头文件。c.头文件用于声明而不是用于定义,因为头文件包含在多个源文件中,所以不应该含有变量或函数的定义。对于头文件不应该含有定义这一规则,有三个例外。头文件可以定义类、值在编译时就已知道的 const 对象和 inline 函数。这些实体可在多个源文件中定义,只要每个源文件中的定义是相同的。
d.一些 const 对象定义在头文件中:
1.当该 const 变量是用常量表达式初始化时,可以保证所有的变量都有相同的值。但是在实践中,大部分的编译器在编译时都会用相应的常量表达式替换这些 const 变量的任何使用。所以,在实践中不会有任何存储空间用于存储用常量表达式初始化的 const 变量。
2.如果 const 变量不是用常量表达式初始化,那么它就不应该在头文件中定义。相反,和其他的变量一样,该 const 变量应该在一个源文件中定义并初始化。应在头文件中为它添加 extern 声明,以使其能被多个文件共享。
const double pi = 3.1416;//可以定义在头文件中const double sq2 = sqrt(2.0);//不可以定义在头文件中17.预处理器:#include 设施是C++ 预处理器的一部分。预处理器处理程序的源代码,在编译器之前运行。
a.设计头文件时,应使其可以多次包含在同一源文件中,这一点很重要。我们必须保证多次包含同一头文件不会引起该头文件定义的类和对象被多次定义。使得头文件安全的通用做法,是使用预处理器定义头文件保护符。头文件保护符用于避免在已经见到头文件的情况下重新处理该头文件的内容
b.头文件应该含有保护符(为防止头文件被同一源文件多次包含而定义的预处理器变量。),即使这些头文件不会被其他头文件包含。编写头文件保护符并不困难,而且如果头文件被包含多次,它可以避免难以理解的编译错误。
#ifndef SALESITEM_H #define SALESITEM_H // Definition of Sales_itemclass and related functions goes here #endif /* 1.测试 SALESITEM_H 预处理器变量是否未定义。如果 SALESITEM_H 未定义,那么 #ifndef 测试成功,跟在 #ifndef 后面的所有行都被执行,直到发现 #endif。相反,如果 SALESITEM_H 已定义,那么 #ifndef 指示测试为假,该指示和 #endif 指示间的代码都被忽略。 2.为了保证头文件在给定的源文件中只处理过一次,我们首先检测 #ifndef。第一次处理头文件时,测试会成功,因为 SALESITEM_H 还未定义。下一条语句定义了 SALESITEM_H。那样的话,如果我们编译的文件恰好又一次包含了该头文件。#ifndef 指示会发现 SALESITEM_H 已经定义,并且忽略该头文件的剩余部分。 */