第1章 开始
iostream库包含两个基础类型istream和osstream,分别表示输入流和输出流。
- cin 输入
- cout 输出
- cerr 输出警告和错误信息
- clog 输出程序运行时的一般性信息
读取数量不定的输入数据: while循环会一直执行直至文件结束符(command + D)
while(std::cin >> value)
包含来自标准库的头文件时,也应该用尖括号(<>)包围头文件名。对于不属于标准库的头文件,则用双引用号("")包围。
第2章 C++基础
带符号类型和无符号类型: 除去布尔型和扩展的字符型外,其他整形可以划分为带符号的(signed)和无符号的(unsigned)两种。带符号类型可以表示正数、负数或0,无符号类型则仅能表示大于等于0的值。
类型所能表示的值的范围决定了转换的过程:
- 当我们把一个非布尔类型的算术值赋给布尔类型时,初始值为0则结果为false,否则结果为true。
- 当我们把一个布尔值赋给非布尔类型时, 初始值为false则结果为0,否则结果为1.
- 当我们把一个浮点数赋给整数时,进行了近似处理。结果值将仅保留浮点数中小数点之前的部分。
- 当我们吧一个整数赋给浮点数时,小数部分记为0。如果该整数所占空间超过了浮点类型的容量,精度可能有损失。
- 当我们赋给无符号类型一个超出它表示范围的值时,结果是初始值对无符号类型表示数值总数取模后的余数。
- 当我们赋给带符号类型一个超出它表示范围的值时,结果是未定义的。
当一个算术表达式中既有无符号数又有int值时,那个int值就会转换成为无符号数。
有两类字符程序员不能直接使用:
- 不可打印的字符,如退格或其他控制字符。
- 含有特殊含义的字符(单引号、双引号、问号、反斜线)
在C++语言中,初始化和赋值是两个完全不同的操作。初始化不是赋值,初始化的含义是创建变量的时候赋与一个初始值,而赋值的含义是把对象的当前值擦除,而以一个新值来代替。
列表初始化
int units_sold = 0;
int units_sold = {0};
int units_sold{0};
如果想声明一个变量而非定义它,就在变量名前添加关键字extern, 而且不要显示地初始化变量:
extern int i; //声明i而非定义i
引用
引用为对象起了另外一个名字,引用类型引用另外一种类型。
引用必须初始化。
一般在初始化变量时,初始值会被拷贝到新建的对象中。然而定义引用时,程序把引用和它的初始值绑定在一起,而不是将初始值拷贝给引用。一旦初始化完成,引用将和它的初始值对象一直绑定在一起。
引用并非对象,相反的,它只是一个已经存在的对象所以的另外一个名字。
引用只能绑定在对象上,而不能与字面值或某个表达式的计算结果绑定在一起。
指针
指针式志向另外一种类型的复合类型。与引用类似,指针也实现了对其他对象的间接访问。然而指针与引用相比又有很多不同点:
- 指针本身就是一个对象,允许对指针进行赋值和拷贝,而且在指针的生命周期内它可以先后指向几个不同的对象。
- 指针无须在定义时赋值。和其他内置类型一样,在块作用域内定义的指针如果没有被初始化,也将拥有一个不确定的值。
解引用:如果指针指向了一个对象,则允许使用解引用符(操作符*)来访问该对象:
int ival = 42;
int *p = &ival;
cout << *p;
新标准下,空指针使用nullptr
指针和引用最大的一点不同: 引用本身不是一个对象,一旦定义了引用,就无法令其再绑定到另外的对象,之后每次使用这个应用都是访问它最初绑定的那个对象。
赋值永远改变的是等号左侧的对象
void* 是一种特殊的指针类型,用于存放任意对象的地址
指向指针的引用:
int i = 42;
int *p;
int *&r = p; //r是一个对指针的引用
要理解r的类型到底是什么,最简单的办法是从右向左阅读r的定义。离变量名最近的符号对变量的类型有直接的影响,因此r是一个引用。符号* 说明r引用的是一个指针。最后int说明,r引用的是一个int指针。
const限定符号
因为const对象一旦创建后其值就不能再改变,所以const对象必须初始化。
在不改变const对象的操作中还有一种是初始化,如果利用一个对象去初始化另外一个对象,则他们是不是const都无关紧要。
int i = 42;
const int ci = i;
int j = ci;
默认情况下,const对象被设定为仅在文件内有效。当多个文件中出现了同名的const变量时,其实等同于在不同文件中分别定义了独立的变量。
extern const int bufSize = fcn();
const的引用 --常量引用
可以把引用绑定到const对象上,就像绑定到其他对象上一样,我们称之为对常量的引用。与不同引用不同的是,对常量的引用不能用作修改它所绑定的对象。
允许将const int&绑定到一个普通int对象上,不允许 非常量引用绑定到常量对象
int i = 42;
const int &r1 = i;
指针和const
指向常量的指针: 不能用于改变其所指对象的值。
const double pi = 3.14
double *ptr = π
const double *cptr = π
*cptr = 42;
const指针: 必须初始化,而且一旦初始化完成,则它的值就不能再改变。把* 放在const之前用以说明指针是一个常量。
int errNumb = 0;
int *const curErr = &errNumb; //curErr 将一直指向errNumb
const double pi = 3.14159;
const double *const pip = π // pip是一个指向常量对象的常量指针。
顶层const
顶层表示指针本身是个常量,底层表示指针所指对象是一个常量。
用于声明引用的const都是底层const。
当执行对象的拷贝操作时,顶层const不受影响,底层const相同的数据类型。一般来说,非常量可以转换成常量,反之则不行:
int i = 0;
int *const p1 = &i;
const int ci = 42;
const int *p2 = &ci;
const int *const p3 = p2;
const int &r = ci;
int *p = p3;//错误,p3包含底层const的定义,p没有
p2 = p3; // p2和p3都是底层const
p2 = &i; //int *能转换成const int*
int &r = ci; //错误 普通int不能绑定到int常量
const int &r2 = i; //正确 const int&可以绑定到一个普通int上
constexpr和常量表达式
常量表达式是指值不会改变并且在编译过程就能得到计算结果的表达式。
字面值属于常量表达式,用常量表达式初始化的const对象也是常量表达式。
const int max_files = 20; //常量
const int limit = max_files + 1; //常量
int staff_size = 27; //非常量
const int sz = get_size(); //非常量,运行时才能得到结果。
constexpr变量:允许将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式。声明为constexpr的变量一定是一个常量,并且必须用常量表达式来初始化。
constexpr int mf = 20;// 20时常量表达式
constexpr int limit = mf + 1; // mf + 1时常量表达式
constexpr int sz = size(); //只有当size是一个constexpr函数才是一条正确的声明语句。
字面值类型
只有字面值类型可以被定义为constexpr类型
算术类型、引用和指针都属于字面值类型。自定义类Sales_item、IO库、string类型则不属于字面值类型,也就不能被定义为constexpr类型。
在constexpr声明中如果定义了一个指针,限定符constexpr仅对指针有效,与指针所指对象无关。
const int *p = nullptr;// p是一个指向整型常量的指针。
constexpr int *q = nullptr; // q是一个指向整数的常量指针。
constexpr把他所定义的对象置为了顶层const
类型别名
- typedef
typedef double wages; //wages时double的同义词
typedef wages base, *p;// base时wages的同义词,p时double*的同义词;
- using关键词
using SI = Sales_item; //SI时Sales_item的同义词。
typedef char *pstring;
const pstring cstr = 0; // cstr是指向char的常量指针
const pstring *ps; // ps是一个指针,它的对象是指向char的常量指针
上述两条声明语句的基本数据类型都是 const pstring,const是对给定类型的修饰符,pstring是指向char的指针,因此加上const就是指向char的常量指针。
auto类型说明符
auto让编译器通过初始值来推算变量的类型,auto定义的变量必须有初始值。
auto会忽略顶层const,同时底层const则会保留下来,如果需要推断auto类型是一个顶层const,需要明确指出
const int ci = i;
auto e = &ci; // d是一个指向整数常量的指针(对常量对象取地址是一种底层const)
const auto f = ci;
decltype 类型指示符
选择并返回操作数的数据类型。在此过程中,编译器分析表达式并得到它的类型,却不实际计算表达式的值。
decltype(f()) sum = x;//sum的类型就是函数f的返回类型。
decltype处理顶层const和引用的方式与auto些许不同。如果decltype使用的表达式是一个变量,则decltype返回该变量的类型(包括顶层const和引用在内)。
如果decltype使用的是一个不加括号的变量,得到的结果就是该变量的类型;如果给变量加上了一层或多层括号,编译器会把它当成是一个表达式。变量是一种可以作为赋值语句左值的特殊表达式,所以这样的decltype就会得到引用类型。
decltype((i))d; //错误: d是int&,必须初始话
decltyoe(i)e; // 正确,e是int
decltype((variable))的结果永远是引用,而decltype(variable)结果只有当variable本身就是一个引用时才是引用
编写自己的头文件
头文件保护符: 确保头文件多次包含仍能安全工作
#ifndef SALES_DATA_H
#define SALES_DATA_H
...
#endif
第一次包含Sales_data.h时,#ifndef的检查结果是真,预处理器将顺序执行后面的操作直到遇到#endif为止。