C++ Primer 中文版 (第5版)
电子工业出版社
C++ 11
标准库iostream定义了4个IO对象:cin、cout、cerr(输出警告和错误)、clog(输出程序运行时的一般性信息,存入日志文件中);
std::cerr << "Error!" << std::endl;
std::cout << "Enter two numbers: " << std::endl;
输出运算符 <<左侧必须是一个iostream对象,右侧的运算对象是要打印的值,运算符将给定的值写到给定的iostream对象中,计算结果就是其左侧运算对象,返回左侧的运算对象,第一个运算符的结果成为第二个运算符的左侧运算对象;运算符endl被称为操纵符,写入endl的效果是结束当前行,并将设备关联的缓冲区中的内容刷到设备中;
调试时,应该保证“一直”刷新流,否则如果程序崩溃,输出可能还在缓冲区,程序崩溃位置的推断可能是错的;
读取输入数量不确定的输入数据,循环条件检测的是std::cin,检测流的状态。如果流是有效的,则检测成功,当遇到文件结束符(Windows系统中是ctrl + Z,然后按enter),或无效的输入(读入的值不是一个整数),istream对象的状态会变为无效:
int v = 0; while (std::cin >> v){ ... }
计算机可寻址的最小内存快称为字节byte,由8 比特bit构成;
存储的基本单元称为字word,通常由几个字节组成,由32或64bit构成;
除bool型和扩展的字符型外,其他整型可以划分为有符号和无符号两种;
无符号的仅能表示大于等于零的值;
与其他整型不同,字符型被分为三种:char、signed char、unsigned char,但字符的表现形式只有两种:带符号和无符号的;
char会表现为带符号或无符号的,具体由编译器决定,不建议在算术表达式中使用char(可以指定signed char 或unsigned char);
选择数据类型的经验准则:
当明确知道数值不可能为负数时,选用无符号类型;
算术表达式中,不要使用char 或 bool;
浮点数选用double,float精度不够,但两者的计算代价差不多;
类型转换:
当给无符号类型赋值一个超出表示范围的值时,结果是对无符号类型表示数值总数取模后的余数;
当给带符号类型赋值一个超出表示范围的值时,结果是未定义的,程序可能继续工作、崩溃、生成垃圾数据;
如果表达式中即有带符号的,也有无符号的,带符号数会自动转换为无符号数;
字面值常量:
定义于任何函数体之外的变量,默认初始化为0;
定义于函数体内部的内置类型的变量将不被初始化;
变量声明与定义:
为了允许把程序拆分成多个逻辑部分来编写,C++支持将程序分割为若干个文件,每个文件单独编译;
声明:使得名字为程序所知,一个文件如果想使用别处定义的名字,则必须包含对名字的声明;
定义:创建与名字关联的实体,申请存储空间,并可能为变量赋初始值;
任何包含显式初始化的声明,将成为定义,给extern标记的变量赋初始值,那就变成定义了;
在函数体内部,如果试图初始化extern标记的变量,将引发错误;
变量只能被定义一次(仅在一个文件中被定义),但是可以被多次声明;
extern int i; // 声明i,不是定义i int j; // 声明并定义j extern int k = 1; // 定义
标识符:
由字母、数字、下划线构成,必须字母或下划线开头;
长度没有限制,但是对大小写敏感;
C++为标准库保留了一些名字,自定义的标识符不能连续出现两个下划线,也不能以下划线紧连大写字母开头;
定义在函数体外的标识符不能以下划线开头;
变量名一般用小写字母,如index;
类名一般以大写字母开头;
作用域:
大多数作用域以花括号分隔;
名字的有效区域从名字的声明开始,以声明语句所在的作用域末端结束;
作用域中一旦声明了某个名字,内层作用域都能访问该名字,同时允许内层作用域中重新定义外层作用域已有的名字;
指针与引用的区别:
int ival = 42; int *p = &ival; // p存放着ival地址 cout << *p; // 得到指针p所指的对象 *p = 0; // 经由p为ival赋值
pi = &ival; // pi指向了ival *pi = 0; // ival的值被改变,指针pi并没有变
两个类型相同的指针,可以用==或!=比较,如果指针存放的地址相同,则他们相等;
如果一个指针指向某对象,另一个指针指向另外对象的下一个地址,可能两个指针值相等;
void*指针:
是一种特殊的指针类型,可以存放任意对象的地址,但存放的是什么类型的对象并不了解;
不能直接操作void*指针所指的对象,因为不知道这个对象是什么类型;
指向指针的指针
指向指针的引用:
引用本身不是一个对象,所以不存在指向引用的指针;
指针是对象,所以存在对指针的引用;
从右向左阅读r的定义,离变量名最近的符号是&,因此r是一个引用,*说明r引用的是一个指针;
ci的常量特征仅仅在执行改变ci操作时才会发挥作用;
默认状态下,const对象仅在文件内有效,当多个文件中出现了同名的const变量,等同于在不同文件中分别定义了独立的变量;
const int a = 5;
编译器将在编译过程中把用到该变量的地方都替换为对应的值;
某些时候,const变量的初始值不是常量表达式,且需要在文件间共享,只在一个文件中定义,在其他多个文件中声明并使用,解决的办法是:const变量不管是声明还是定义,都添加extern关键字;
const的引用
把引用绑定到const对象上,称之为对常量的引用;
int &ri = dval;
此时ri绑定的是临时量,该操作是非法的;
顶层const
顶层const表示指针本身是个常量;
底层const表示指针所指的对象是一个常量;
执行对象的拷贝操作时,拷入和拷出的对象必须是具有相同的底层const资格,或者两个对象的数据类型必须能够转换(非常量可以转常量,反之不行);
constexpr和常量表达式
常量表达式(const expression)是指值不会改变并且在编译过程就能得到计算结果的表达式;
字面值属于常量表达式,用常量表达式初始化的const对象也是常量表达式;
constexpr变量
C++11中规定,允许将变量声明为constexpr类型,以便由编译器来验证变量的值是否是一个常量表达式;
声明为constexpr的变量一定是一个常量,而且必须用常量表达式初始化;
constexpr函数:足够简单,编译时就可以计算其结果;
字面值类型
包括算术类型、引用、指针这类简单、值也显而易见的,不包括自定义类、IO库、string等(不能被定义为constexpr);
一个constexpr指针的初始值必须是nullptr或0,或者是存储于某个固定地址中的对象;
函数体内定义的变量一般来说并非存放在固定地址中,因此constexpr指针不能指向这样的变量;
相反的,定义于所有函数体之外的对象其地址固定不变,能用来初始化constexpr指针;
允许函数定义一类有效范围超出函数本身的变量,这类变量和定义在函数体之外的变量一样有固定地址,因此constexpr引用能绑定到这样的变量上,constexpr指针也能指向这样的变量;
指针和constexpr
在constexpr声明中,如果定义了一个指针,限定符constexpr仅对指针有效,与指针所指的对象无关;
类型别名:是某种类型的同义词,用法同原类型;
有两种方法定义类型别名:
传统的方法是用关键字typedef;
新标准规定了新的方法,使用别名声明来定义,使用关键字using;
指针、常量和类型别名(太绕了,和直觉理解不一样……)
auto类型说明符
把表达式的值赋值给变量时,让编译器通过初始值来推断表达式的类型;
auto定义的变量必须有初始值;
auto能在一条语句中声明多个变量,基本数据类型需要相同;
使用引用,其实是使用引用的对象,被用作初始化值时,真正参与初始化的其实是引用对象的值,编译器以引用对象的类型作为auto的类型;
auto一般会忽略掉顶层const,底层const会保留下来;
还可以将引用的类型设为auto: