C++基础(1~7)
第1章 开始
1.1
-
在UNIX和Windows系统中,执行完一个程序后,都可以通过
echo
命令获取其返回值:UNIX:
$ echo $?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-77UZF17I-1588660354488)(C:\Users\h\AppData\Roaming\Typora\typora-user-images\image-20200410083217521.png)]
Windows:
$echo %ERRORLEVEL%
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hJIdpMFy-1588660354498)(C:\Users\h\AppData\Roaming\Typora\typora-user-images\image-20200410082302097.png)]
1.2 初识输入输出
- std::endl(缓冲刷新操作符)的效果是结束当前行,并将与设备关联的缓冲区中的内容刷到设备中。
1.4 控制流
-
读取数量不定的输入数据:
cin
语句当遇到EOF(文件结束符,UNIX下ctrl+D可结束),或遇到无效输入时会将返回的istream
状态变为无效。int main() { int value; while (std::cin >> value) cout << value << " "; cout << endl; }
第2章 变量和基本类型
2.1 基本内置类型
-
char类型实际上有可能会表现为signed char或unsigned char,由编译器决定。
-
如何选择内置类型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zITVbrmB-1588660354504)(C:\Users\h\AppData\Roaming\Typora\typora-user-images\image-20200410091439378.png)]
-
当我们赋给无符号类型一个超出它表示范围的值时,结果是初始值对无符号类型表示数值总数取模后的余数;而当赋给带符号类型一个超出它范围的值时,结果是未定义的(这里有问题,实际上当给两者赋予超出范围值都是未定义的,但这个范围与它们能表示的最大值决定,如signed char表示-128~127,所以128表示的是-128,255表示-1,256则超出范围,这都与有符合以补码形式保存有关)。
-
当一个算术表达式上又有无符号数又有int值,int值会转换为无符号数。
2.2 变量
-
分离式编译:如果想声明一个变量而非定义它,就在变量名前添加关键字extern,且不要显示初始化(否则会变为定义)。
-
在函数体内部,不能初始化一个由extern关键字标记的变量,会报错(为什么?)。
-
建议:第一次使用变量时才定义它。
-
列表初始化:
// 首先明确四种初始化语句 int a = 0; int a = {0};// 这种就是列表初始化 int a{0}; // 这种就是列表初始化 int a(0);
long double ld = 3.1415926536; int a = {ld}; // 错误:存在丢失信息危险会报错 int b{ld}; // 错误:存在丢失信息危险会报错 int c = ld; // 正确
2.3 复合类型(引用和指针)
- tips:从右向左阅读复杂的类型声明语句,更容易读出它的类型。‘
2.4 const
-
const对象仅在文件内有效:当多个文件中出现了同名的const变量时,其实等同于在不同文件分布定义了独立的变量。
如果想在多个文件之间共享const对象,必须在变量的定义之前添加extern关键字:
// file_1.cpp定义并初始化了一个常量,该常量能被其他文件访问 extern const int bufSize = fcn(); // file_l.h头文件声明 extern const int bufSize;
-
const引用(指针也是如此)允许引用类型不一致,只要该表达式的结果能转换成引用的类型:
double dval = 3.14;
const int &ri = dval;
其上相当于生成了一个const int类型的临时量对象,所以不允许非const引用引用这个临时量对象。
指针和引用都规定,类型必须与所指对象的类型一致,以上是一个例外,还有另一种例外就是向下转型。
-
顶层const和底层const
-
constexpr:将变量声明为constexpr类型,编译器则可以验证变量是否为常量表达式(不是则报错)。
constexpr会将指针变量都声明为顶层const。
2.5 处理类型
类型别名
- typedef:
typedef double wages, *pd;
- 新标准规定的别名声明:
using wages = double;
auto类型说明符
- auto能让编译器替我们去分析表达式所属的类型。
- auto定义的变量必须有初始值。
- auto会忽略顶层const,保留底层const(也很好理解为什么),如果希望是一个顶层const,则显式const(引用也会被忽略,也是通过显式声明)。
decltype类型指示符
-
decltype的作用式选择并返回操作数的数据类型(仅分析类型,不计算表达式的值):
decltype(f()) sum = x;
-
decltype不会忽略顶层const和引用,也就是它返回是实际类型,而不是像auto选择更适合的类型进行优化。
-
int i = 42, &r = i, *p = &i; decltype(r + 0) b; //这时类型就不是引用而是int了 decltype(*p) c = 1; //如果表达式的内容是解引用操作,则会得到引用类型,所以这里为int &类型
-
decltype((varible))的结果永远是引用。给变量加上一层或多层括号,编译器就会把它当成一个表达式,变量是一种可以作为赋值语句左值的特殊表达式,所以这样的decltype就会得到引用类型。
第3章 字符串、向量和数组
3.2 标准库类型string
-
getline(cin, line)
string line; while (getline(cin, line)) { cout << line << endl; }
-
string::size_type类型:其size()函数的返回类型。
这是个无符号数,注意避免与有符号数混用从而造成错误!
-
标准库允许把字符字面值和字符串字面值转换成string。
string s1 = ","; string s = "abc" + s + 'd'; // 必须保证运算符两边有其一为string类型(非字面值)
因为字符串字面值并不是string的对象(与C兼容)!
-
#include 定义了一组处理字符的函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fuo6fSFf-1588660354508)(C:\Users\h\AppData\Roaming\Typora\typora-user-images\image-20200413201358523.png)]
3.3 标准库类型vector
- 不能在范围for循环中向vector对象添加元素:在范围for语句中,预存了end()的值,添加或删除元素后,这个值就变为无效了。
3.4 迭代器介绍
-
任何一种可能改变vector对象容量的操作(如push_back)都会使该vector对象的迭代器失效。
谨记,但凡是使用了迭代器的循环体,都不要向迭代器所属的容器添加元素!
-
defference_type:带符号整数,表示迭代器的距离,
iter1 - iter2
返回该类型。
3.5 数组
- 默认情况下,数组的元素被默认初始化。
- 数组间不允许拷贝和赋值。
- 默认情况下,类型修饰符从右向左依次绑定,所以可以从右向左阅读来识别其类型。如
int *ptrs[10]
为一个数组,该数组元素为指针(*),该指针指向int类型;而int (*pArr)[10]
首先为一个指针(括号括住),然后该指针指向一个数组([]),数组元素为int类型。 - 指针与数组:一般来说使用数组名作为赋值,都会将其替换成指向该数组首元素的指针。不过在使用decltype时却会帮其转换成数组类型。(就很神奇,多维数组的数组名则是替换成指向该数组首数组的指针)。
- 只要两个指针指向同一数组就可以进行比较以及进行相减(返回ptrdiff_t类型,带符号类型)。
3.6 多维数组
-
使用范围for处理多维数组:
for (auto &row : ia) { // 注意这里必须使用引用,否则row的类型是指针而不是数组,会导致后面无法使用范围for for (auto col : row) { ... } }
-
类型别名多维数组的指针:
using int_array = int[4]; // 两者皆可 typedef int int_array[4];
第4章 表达式
4.1 基础
- 左值和右值:当一个对象被用作右值时,用的是对象的值(内容);当对象被用作左值时,用的时对象的身份(在内存的位置)。
- 使用关键字decltype时,如果表达式结果是左值,则返回一个引用类型,否则返回原类型。
- 只有4种运算符明确规定了求值顺序:
&&
、||
、?:
、,
。
4.5 递增和递减运算符
- 递增的后置版本优先级高于解引用运算符;但前置版本好像是相同的,遵循右结合律。
4.7 条件运算符(?:)
-
条件运算符的优先级比较低,所以要注意加括号!!
cout << (grade < 60 ? "fail" : "pass") << endl; // 输出条件运算的结果 cout << (grade < 60) ? "fail" : "pass" << endl; // 输出1或者0,然后cout进行条件运算 cout << grade < 60 ? "fail" : "pass" << endl; // 输出grade,然后cout<60进行条件运算
4.8 位运算符
- 位运算对于有符合数的符号位如何处理没有明确规定,是一种未定义行为,所以建议将位运算符用于处理无符号数。
- 要注意<<和>>的优先级可能比较高,在作为IO运算符时要注意为一些运算加括号,避免错误。
4.11 类型转换
- 算术类型之间的隐式转换会尽可能避免损失精度,如果表达式种既有浮点数又有整数,会统一为浮点数。
- 在大多数表达式中,比int类型小的整数型会首先提升为较大的整数类型(如short、char以及bool等都会转成int类型)。转换为int、unsigned int、long、unsigned long、long long和unsigned long long中能容纳其全部可能的值的最小类型。
- 如果一个运算对象为无符号,另一个为有符号,且其中无符号类型不小于带符号类型,那么带符号转为无符号的;如果大于,则看带符号类型是否包含无符号的所有值,否则同样转为无符号。
- static_cast会关闭精度损失的警告信息,其实用起来挺危险的。static_cast还可以将void *转为任意类型指针(隐式不行)。
第5章 语句
5.3 条件语句
- switch内部的变量定义:如果在某处一个带有初值的变量位于作用域之外,在另一处该变量位于作用域之内,则从前一处跳转到后一处的行为是非法行为。
5.6 try语句块和异常处理
- throw表达式引发一个异常,try检测,catch处理。
- 最终没有被处理的异常,程序会转到名为terminate的标准库函数,没有try的语句块发生异常也是如此。
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iPoPZQ15-1588660354510)(C:\Users\h\AppData\Roaming\Typora\typora-user-images\image-20200421090205691.png)]
第6章 函数
6.2 参数传递
-
使用引用避免拷贝:建议使用常量引用。
-
返回额外信息:比较简单的方法,使用引用参数。
-
由于顶层const会被忽略,所以const int和int是同一种参数列表。
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RnA9fKdu-1588660354515)(C:\Users\h\AppData\Roaming\Typora\typora-user-images\image-20200421102119636.png)]
-
数组引用形参:
void func(int (&arr)[10])
-
main处理命令行选项:第一个形参argc表示数组中字符串的数量;第二个形参为字符串指针,第一个指针指向程序名,最后一个指针必定为0(不包含在argc数量里面)。
-
initializer_list实现可变形参,有点像vector。
-
省略符(…)形参:varargs来访问省略的形参所接受的实参。
6.3 返回类型和return语句
-
函数的返回类型为引用时得到左值,否则得到右值。
-
返回数组指针:可以使用类型别名来避免出错。不使用的话如下:
int (*func(int i))[10];
-
尾置返回类型:
auto func(int i) -> int(*)[10]
6.4 函数重载
-
const_cast和重载:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yuJz6ejo-1588660354519)(C:\Users\h\AppData\Roaming\Typora\typora-user-images\image-20200422172617493.png)]
-
如果在内层作用域声明名字,它讲隐藏外层同名声明,从而导致忽略掉其他同名函数重载。
6.5 特殊用途语言特性
默认实参
- 默认实参负责填补函数调用的尾部实参,所以提供实参的这个参数前面的参数也必须提供实参,否则报错。
- 局部变量不能作为默认实参(全局变量,调用函数都可)。
constexpr函数
- 指能用于常量表达式的函数,函数的返回类型和所有形参的类型都是字面量类型,且有且只有一条return语句。
- 内联函数和constexpr函数通常放在头文件内,因为他们需要在程序中多次定义(为了让每个文件独立编译时,编译器可以展开函数),并且多个定义完全一致。
6.6 函数匹配
-
如果有且只有一个函数满足下列条件,则匹配成功:
- 该函数每个实参的匹配都不劣于其他可行函数需要的匹配。
- 至少有一个实参的匹配优于其他可行函数提供的匹配。
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wVbcTqvG-1588660354521)(C:\Users\h\AppData\Roaming\Typora\typora-user-images\image-20200421165123949.png)]
6.7 函数指针
-
声明方式:只需要用指针替换函数名即可。
// 函数指针 bool (*pf)(const string &, const string &); // 函数 bool lengthCompare(const string &, const string &);
-
当把函数名作为一个值使用时,会自动转换成指针(使用取地址符也可);对于函数指针也是如此,会自动转换成函数直接使用。
-
函数指针可以作为形参,也可作为返回值。
第7章 类
7.1 定义抽象数据类型
- 编译器创建的构造函数被称为合成的默认构造函数,他会默认初始化数据成员(除非存在类内初始值,就是定义数据成员就赋值)。
- 只有类没有声明任何构造函数时,编译器才会自动生成默认构造函数。
- 定义在类内部的成员函数都是自动inline的。
7.3 类的其他特性
- 可变数据成员:使用mutable修饰数据成员,该成员可以在一个const成员函数中被改变。
- 返回*this的成员函数:返回类型必须是引用类型,这样就能实现链式操作。
- 基于const的重载:对于一个const的成员函数,返回的*this也必须是const引用的,所以可以重载该成员函数。通过对象本身是否为const决定调用哪个成员函数,从而也能够实现链式调用。
7.5 构造函数再探
-
如果成员是const或者是引用的话,或者成员类类型没有定义默认构造函数,都必须在构造函数上使用初始化列表为这些成员提供初值(而不能在函数体进行赋值操作)。
建议:使用构造函数初始值。
-
构造函数初始化列表的初始化顺序,与他们被定义的顺序有关,而与他们在初始化列表中的顺序无关。
-
委托构造函数:
Sales_data(): Sales_data("", 0, 0); // Sales_data(string, unsigned, double)是一个已经被定义的构造函数。
-
转换构造函数:当类中的构造函数只接受一个实参时,实际上定义了该实参类型转换为此类类型的隐式转换机制。例如,定义了
Sales_data(string )
,则可以使string类型的变量隐式转换成Sales_data
。 -
上述转换只允许一步。例如,char *能转为string,此时不能将char *直接转为
Sales_data
。 -
通过
explicit
关键字可以阻止含有单实参的构造函数所带来的隐式转换(仍可以显示转换static_cast)。并且该构造函数只能以直接初始化的方式使用。 -
聚合类与字面值常量类(暂时略过)。
7.6 类的静态成员
- 静态数据成员可以是不完全类型(特别的,静态数据成员可以是它所属的类)。静态成员还能作为默认实参(非静态不能的原因是它的值本身就属于对象的一部分,这样就不能提供一个对象以便从中获取成员的值)。
下一部分:C++标准库(8~12)
End