挑选了一些C++primer第五版第二章的一些我认为重要或者看起来两眼冒星星的知识点,感觉目前还是不太理解,但是没关系,放在这里以后慢慢看~
1. 字符和字符串字面值
由单引号括起来的一个字符称为char型字面值,双引号括起来的零个或多个字符则构成字符型字面值。
字符串字面值的类型实际上是由常量字符构成的数组,编译器在每个字符串的结尾处添加一个空字符('\0'),因此,字符串字面值的实际长度要比它的内容多1.如果两个字符串字面值位置紧邻且仅由空格、缩进和换行符分割,则它们实际上是一个整体。
2. 变量声明和定义的关系
变量声明规定了变量的类型和名字,定义还申请存储空间,也可能会为变量赋一个初始值。
如果想声明一个变量而非定义它,就在变量名前添加关键字extern。例如,
extern int i; //声明i而非定义i
int j; //声明并定义j
变量能且只能被定义一次,但是可以被多次声明。
3. 复合类型
两种常用的复合类型:引用和指针。
(1)引用
通过将声明符写成&d的形式,其中d是变量名。例如,
int ival=1024;
int refVal=ival; //refVal指向ival(是ival的另一个名字)
定义引用时,程序将引用和它的初始值绑定在一起,而不是将初始值拷贝给引用,因此,无法令引用重新绑定到另一个对象,因此,应用必须初始化。而且,引用只能绑定在对象上,而不能与字面值或某个表达式的结果绑定在一起。另外,引用本身不是一个对象,因此不能定义引用的引用。
(2)指针
通过将声明符写成*d的形式,其中d是变量名。例如
int *p1,*p2; //p1和p2都是指向int型对象的指针
指针本身是一个对象,允许对指针赋值和拷贝,而且在指针的生命周期内可以先后指向几个不同的对象。
指针存放某个对象的地址,要想获得该地址,需要使用取地址符(&)。例如
int ival=42;
int *p=ival; //p存放变量ival的地址(p是指向变量ival的指针)
不能定义指向引用的指针,因为引用不是对象,没有实际地址。
指针的类型要和它所指向的对象严格匹配。
如果指针指向了一个对象,则允许使用解引用符(*)来访问该对象。例如
int ival=42;
int *p=ival; //p存放变量ival的地址(p是指向变量ival的指针)
cout << *p; //由符号*得到指针p所指对象,输出42
void *是一种特殊的指针类型,可以存放任意对象的地址(可以不知道该地址中是什么类型的对象)。但是该指针的作用有限,可以拿它和别的指针比较、作为函数的输入或输出,或者赋给另外一个void *指针。但是不能直接操作void *指针所指的对象,因为我们并不知道这个对象到底是什么类型,也就无法确定能在这个对象上做哪些操作。
4. const限定符
使用方法如下,
const int bufSize=512;
(1)初始化
const对象一旦创建后其值就不能再改变,所以const对象必须初始化。如果利用一个对象去初始化另外一个对象,则它们是不是const都无关紧要。
默认状态下,const对象仅在文件内有效。如果想在多个文件之间共享const对象,必须在变量的定义之前添加extern关键字。
(2)const的引用
不能让一个非常量引用指向一个常量对象,但是允许一个常量引用绑定非常量对象、字面值,甚至是一个一般表达式。例如
const int ci=1024;
const int &r1=ci; //正确,引用及其对应的对象都是常量
int &r2=ci; //错误,试图让一个非常量引用指向一个常量对象
int i=42;
const int &r3=i; //正确,允许将const int&绑定到一个普通int对象上
const int &r4=42; //正确,允许将const int&绑定到一个int字面值上
const int &r5=r3*2; //正确,允许将const int&绑定到一个一般表达式上
(3)指针和const
指向常量的指针不能用于改变其所指对象的值,要想存放常量对象的地址,只能使用指向常量的指针。
允许把指针本身定义为常量。常量指针必须初始化,而且一旦初始化完成,则它的值(也就是存放在指针中的那个地址)就不能再改变了。例如(把*放在const关键字之前用以说明指针是一个常量,不变的是指针本身的值而非指向的那个值)
int errNumb=0;
int *const curErr=&errNumb;
(4)顶层const和底层const
顶层const表示指针本身是一个常量,而底层const表示指针所指的对象是一个常量。当执行对象拷贝时,底层const和顶层const区别很大。例如
int i=0;
int *const p1=&i; //顶层const,p1值不能改变
const int ci=42; //顶层const,ci值不能改变
const int *p2=&ci; //底层const,p2值可以改变
const int *const p3=p2; //靠右的const是顶层const,靠左的是底层const
const int &r=ci; //用于声明引用的const都是底层const
//当执行对象的拷贝操作时,拷入和拷出的对象必须具有相同的底层const资格,或者两个对象的数据类型必须能够转换
i=ci; //正确,拷贝ci的值。ci是一个顶层const,对此操作无影响
p2=p3; //正确,p2和p3指向的对象类型相同,p3顶层const的部分不影响
int *p=p3; //错误,p3包含底层const的定义,而p没有
p2=&i; //正确,int*能转换成const int*
int &r=ci; //错误,普通的int&不能绑定到int常量上
const int &r2=i; //正确,const int&可以绑定到一个普通int上
哇,真是两眼冒了无数颗星星@_@
5. constexpr和常量表达式
常量表达式是指不会改变并且在编译过程就能得到计算结果的表达式。例如
const int max_files=20; //max_files是常量表达式
const int limit=max_files+1; //limit是常量表达式
int staff_size=27; //staff_size不是常量表达式
在constexpr声明中如果定义了一个指针,限定符constexpr仅对指针有效,而与指针所指的对象无关。例如
const int *p=nullptr; //p是一个指向整型常量的指针
constexpr int *p=nullptr; //q是一个指向整数的常量指针
特别注意,p是一个指向常量的指针,而q是一个常量指针,constexpr将它所定义的对象置为了顶层const。
6. 处理类型
(1)类型别名
有两种方法可用于定义类型别名,一种是typedef,一种是using。例如
typedef double wages; //wages是double的同义词
wages hourly,weekly; //等价于 double hourly,weekly
using SI=Sales_item; //SI是Sales_item的同义词
SI item; //等价于Sales_item item
(2)auto类型说明符
auto类型说明符能让编译器替我们去分析表达式所属的类型。例如
//有val1和val2相加的结果来推断出item的类型
auto item=val1+val2; //item初始化为val1和val2相加的结果
auto声明的变量必须有初始值。使用auto也能在一条语句中声明多个变量。因为一条声明语句只能有一个基本数据类型,所以该语句中所有变量的初始基本数据类型必须都一样。例如
auto i=0, *p=&i; //正确,i是整数,p是整型指针
auto sz=0, pi=3.13; //错误,sz和pi的类型不一致
(3)decltype类型指示符
它的作用是选择并返回操作数的数据类型,在此过程中,编译器分析表达式并得到它的类型,而不实际计算表达式的值。例如
decltype(f()) sum=x; //sum的类型就是函数f的返回类型
如果表达式的内容是解引用操作,则decltype将得到引用类型。例如 decltype(*p) 的结果类型就是int&,而非int。
如果变量名加上了一对括号,则得到的类型与不加括号时会有不同。切记,decltype((variable))(注意是双层括号)的结果永远是引用,而decltype(variable)的结果只有当variable本身就是一个引用时才是引用。
7. 自定义数据结构
以关键字struct开始,紧跟类名和类体(其中类体部分可以为空)。类体由花括号包围形成了一个新的作用域。类体右侧的表示结束的花括号后必须写一个分号,这是因为类体后面可以紧跟变量名以示对该类型对象的定义。例如
struct Sales_data
{
string bookNo;
unsigned units_sold=0;
double revenue =0.0;
};
8. 头文件
头文件一旦改变,相关的源文件必须重新编译以获取更新过的声明。
确保头文件多次包含仍能安全工作的常用技术是预处理器。例如 #include。还会用到的一项预处理功能时头文件保护符,头文件保护符依赖于预处理变量。预处理变量有两种状态:已定义和未定义。#define指令把一个名字设定为预处理变量,另外两个指令则分别检测某个指令的预处理变量是否已经定义:#ifdef当且仅当变量已定义时为真,#ifndef当且仅当变量未定义时为真。一旦检查结果为真,则执行后续操作直至遇到#endif指令为止。一般把预处理变量的名字全部大写。
以上知识点因为没有实际案例的引导可能显得过于空洞,不是很清楚没关系,有些印象就ok了,以后需要用到的时候再回过头来看,会理解得更深刻。