第1章:开始
第2章:变量和基本类型
2.3符合类型
- &、* 意义判断
- 在声明中:构成符合类型(引用、指针)
- 在表达式中:运算符(去地址、解引用)
2.3.1 引用
2.3.2 指针
2.4 const限定符
2.4.3 顶层const
- 顶层const:本身是常量,例如const指针(int *const p ,这里的const是顶层const,指针p不可以改变)
- 底层const:所指向的对象是常量(const int *p,指针p可以改变,指向的量不可以改变)
第3章:字符串、向量和数组
3.4 迭代器介绍
- string、vector都支持下标和迭代器运算
3.4.1 使用迭代器
- begin()与end()函数
vector<int> v;
const vector<int> cv;
auto it1 = v.begin(); //it1的类型是vector<int>::iterator
auto it2 = cv.begin(); //it2的类型是vector<int>::const_iterator
- cbegin()和end()专门得到const_iterator类型
3.5 数组
3.5.1. 定义和初始化内置数组
- 实时性较好,但灵活性较差
- 数组维度必须是常量
- 不可使用auto推断
- 数组不允许拷贝和赋值,不能使用一个数组初始化另一个数组,也不能用数组为其他数组赋值
- 维度 >= 初始值总数量
- 指针数组与数组指针
- 指针数组:是数组,存放指针的数组(例如:int *ptrs[10]; ptrs是含有10个整型指针的数组)【从右往左阅读】
- 数组指针:是指针,指向数组的指针(例如 :int (*parry)[10] = &arr; parry指向一个含有10个整数的数组)【从内往外阅读】
- 例子:int *(&array) [10] = pts; // array是数组的引用,该数组含有10个指针
3.5.2 访问数组的元素
- 用size_t类型定义数组下标(机器相关的无符号类型)
3.5.3 指针和数组
- 数组会转换成指针,替换成指向数组首元素的指针
- 取地址符(&)可以用于数组(取地址符可用于任何对象)
- auto与decltype用于数组时的区别
- auto:用其推断得到指针
- decltype:用其推断得到数组
- 指针也是迭代器
- begin()与end()
不是成员函数,将数组作为他们的参数进行使用(与3.4.1节的区别)
int ia[] = {0,1,2,3,4,5};
int *beg = begin(ia);
int *last = end(ia);
- 指针加上加上某整数值,结果是指针
int arr[5] = {1,2,3,4,5};
int *ip = arr; //等价于int *ip + &arr[0]
int *ip2 = ip+4; //ip2指向arr的尾元素arr[4]
第4章:表达式
4.1 基础
4.1.1基本概念
- 左值和右值
- 当一个对象被当作右值的时候,用的是对象的值(内容);当对象被用作左值的时候,用的是对象的身份(在内存中的位置——地址)。
- 左值可以当成右值使用,但是右值不能当成左值使用。
博客:左值与右值
4.5 递增和递减运算符
- 一般使用前置版本
- 后置与前置的区别
- 后置:先赋值,再自增
- 前置:先自增,再赋值
- 后置与前置的区别
- 混用解引用和递增运算符
后置递增运算符优先级 > 解引用运算符
int arr[] = {1, 2, 3, 4};
int *p = arr;
int a = *p++; //等价于a = *(p++); 即a = *p = 1; p = p + 1,*p=2;
int b = *++p; //等价于b = *(++p) ; 即p = p + 1; b = *p = 3;
- 在程序中,一般使用*p++,简洁
cout << *iter++ << endl; //等价于cout << *iter <<endl; ++iter;
4.11.3 显式转换
- 命名的强制类型转换 ( 注意:避免强制类型转换,只有在函数重载的上下文中使用const_cast合适,其他情况尽量不要使用)
形式:cast-name< type >(expression)(注意:若type是引用类型,结果是左值)- static_cast:任何具有明确定义的类型转换,但不包含底层const
- 适用场景:
- 把较大的算术类型赋值给较小的类型
- 当编译器无法自动执行的类型转换(例如:找回存在于void*指针的值)
- 适用场景:
- const_cast:改变运算对象的底层const
- 使用对象:
- 不是常量:获得写权限是合法行为
- 常量:执行写操作产生未定义的后
- 使用场景
如果有一个函数,它的形参是non-const类型变量,而且函数不会对实参的值进行改动,这时我们可以使用类型为const的变量来调用函数,此时需要使用const_cast
常用于有函数重载的上下文中
- 使用对象:
- dynamic_cast:
- reinterpret_cast:通常为运算对象的为模式提供较低层次上的重新解释
- static_cast:任何具有明确定义的类型转换,但不包含底层const
4.12 运算符优先级表
第5章:语句
第6章:函数
6.1 函数基础
6.2参数传递
- 传值参数:函数对形参的操作不会影响实参
- 指针形参:形参的指针和实参指针,形参的指针是实参指针的拷贝,是两个不同的指针指向相同的值,形参指针的值(地址)改变并不会影响实参的值,改变形参指向对象的值会改变是实参指针指向对象的值
#reset函数接受一个指针,将指针指向的值置为9,并将指针置为0
void reset( int *ip)
{ *ip = 9;
ip = 0; //只改变了ip的局部拷贝,实参未被改变
}
int i = 42;
reset(&i); //改变i的值而非i的地址
cout << i << endl; //i=9
- 传引用参数:引用形参,改变实参
- 好处:
- 避免拷贝:拷贝效率低,且有些类类型不支持拷贝
- 返回额外信息:
- 好处:
- 建议使用引用类型的形参,代替指针
6.3 返回类型和return语句
- return拥有两种类型
- return;( 只能用于void函数)
- return expression;
- 用于void函数中
- 用于函数中部:提前结束函数(相当于break语句);
- 用于函数尾部: expression必须是一个返回void的函数
- 用于非void函数中(此函数必须拥有返回值。main函数除外,因为编译器隐式的插入返回0的return语句)
- expression类型必须和函数的返回类型相同或能隐式转换
- 用于void函数中
- 不要返回局部对象的引用或指针(会引发错误)
6.3.3 返回数组指针
因为数组不能被拷贝,所以不能返回数组。但是函数可以返回数组的指针或者引用
- 定义返回数组的指针或者引用的函数
- 使用类型别名
- 不适用类型别名
- 使用尾置返回类型(个人觉得比较好用)
- 使用decltype(已知返回的指针指向某个数组)
#类型别名
using arrT = int[10];
arrT* func(int i);
#什么也不用
int (*func(int i))[10];
#尾置返回
auto func(int i) -> int(*)[10];
#使用decltype
int odd[] = {1,3,5,7,9};
int even[] = {0,2,4,6,8};
decltype(odd) *arrPtr(int i)
{return (i%2) ? &odd: &even;}
6.4 函数重载
- 函数名相同,形参列表不同
- 顶层const不影响参数传递,底层const会影响
- 拥有顶层const与没有顶层const形参无法区分
- 只通过函数返回值类型不同来进行函数重载会失败
- 重载与作用域
不要定义局部函数声明,调用函数会隐藏全局函数