C++ Primer 读书笔记01
“C++ Primer”
变量声明和定义
-
变量声明规定变量的类型和名字,使得名字为程序所知
-
定义除了与声明相同的部分还申请了存储空间,也可能会为变量赋一个初始值
-
变量能且只能定义一次,但是可以被多次声明
如果想声明一个变量而非定义它,就在变量名前添加关键字
extern
extern int i; // 声明 i 而非定义 i int j; // 声明并定义 j
const
-
const
对象一旦创建后其值就不能再改变,所以const
对象必须初始化 -
默认情况下,
const
对象仅在文件内有效,当多个文件出现了同名的const
变量时,其实等同于在不同文件中分别定义了独立的变量 -
如果想要让
const
在多个文件中共享,则对const
变量不管是声明还是定义都需要添加extern
关键字extern const int bufSize = fcn(); // file.cc 定义并初始化一个常量,该常量能被其他文件访问 extern const int bufSiz; // file.h 与 file.cc 定义的是同一个
指针与 const
-
指向常量的指针不能用于改变其对象的值
const double pi = 3.14; double *ptr = π // 错误 const double *cptr = π *cptr = 4; // 错误 double dval = 3.14; cptr = &dval; // 正确,允许另一个指向常量的指针指向一个非常量对象
-
const 指针
int errNum = 0; int *const curErr = &errNum; // curErr 将一直指向 errNum const double pi = 3.14; const double *const pip = π // pi 是一个指向常量对象的常量指针
-
顶层
const
:表示指针本身是个常量 -
底层
const
:表示指针所指的对象是一个常量
-
数组与指针
-
数组指针
int (*Parray)[10] = &arr; // Parray 指向一个含有 10 个整数的数组 int (&arrRef)[10] = arr; // arrRef 引用一个含有 10 个整数的数组
-
指针数组
int *ptrs[10]; // 含有 3 个整数的数组
begin 和 end (11)
int ia[] = {0,1,2,3};
int *beg = begin(ia); // 指向 ia 首元素的指针
int *last = end(ia); // 指向 ia 尾元素的下一位置的指针,这两个函数定义在 iterator 头文件中
左值
-
可以出现在
operator=
左边 -
当对象被用作左值的时候,用的是对象的身份(在内存中的位置)
右值:
-
可以出现在
operator=
右边 -
当一个对象被用作右值的时候,用的是对象的值(内容)
-
如果表达式的求值结果是左值,
decltype
作用于该表达式(不是变量)得到一个引用类型int *p = nullptr; decltype(*p) x; // 解应用得到的结果是左值,所以 x 是 int& decltype(&p) y; // 取地址运算符生成右值,所以 y 是 int**
赋值运算符
-
赋值运算符的结果是它的左侧运算对象,并且是一个左值
-
如果赋值运算符的左右两个运算对象类型不同,则右侧运算对象将转换成左侧运算对象的类型
-
赋值运算符满足右结合律
int ival, jval; ival = jval = 1; // 正确
递增和递减运算符
-
前置版本:首先将运算对象加1(或减1),然后将改变后的对象作为求值结果已左值返回,
-
后置版本:也会将运算对象加1(或减1),但是求值结果是运算对象改变之前值的副本,是右值
-
如非必须,否则不用递增和递减运算符的后置版本
成员访问运算符
点运算符与箭头运算符的关系:ptr->mem 等价于 (*ptr).mem
sizeof
sizeof
是一个运算符而不是一个函数,返回一条表达式或者一个类型名字所占的字节数,返回值为 size_t
类型。
Sales_data data, *p;
sizeof(Sales_data); // 存储 Sales_data 类型的对象所占的空间的大小
sizeof(data); // data 的类型的大小,即 sizeof(Sales_data)
sizeof p; // 指针所占空间大小
sizeof *p; // p 所指类型的空间大小,即 sizeof(Sales_data)
sizeof data.revenue // Sales_data 的 revenue 成员对应类型的大小
sizeof Sales_data& // 即 sizeof(Sales_data)
- 对数组执行
size_of
运算得到整个数组所占空间的大小,等价于对数组中所有的元素各执行一次size_of
运算并将结果求和 - 对
string
对象或者vector
对象执行size_of
运算只返回该类型固定部分的大小 - 对引用类型执行
size_of
运算得到被引用对象所占空间的大小
强制类型转换
-
static_cast
任何具有明确定义的类型转换,只要不包含底层const
都可以使用double slope = static_cast<double>(j) / i;
-
const_cast
只能改变运算对象的底层const
(即指针指向的对象是一个常量)const char *pc; char *p = const_cast<char *>(pc); // 去掉了 const 属性
-
dynamic_cast
用于动态类型转换。只能用于含有虚函数的类,用于类层次间的向上和向下转化。只能转指针或引用。
向上转换:指的是子类向基类的转换
向下转换:指的是基类向子类的转换
范围 for
for (declaration : expression)
statement
expression
表示的必须是一个序列,如花括号括起来的初始值列表、数组、vector string
等,这些类型的共同特点是拥有能返回迭代器的begin
和end
成员
局部静态对象:将局部变量定义成 static 类型
局部静态对象在程序执行路径第一次经过对象定义语句时初始化,并且知道程序终止时才被销毁,在此期间对象所在的函数结束执行也不会对它有影响。
函数声明
类似于变量,函数只能定义一次,但是可以声明多次
initializer_list
一种标准库类型,用于表示某种特定类型的值的数组,定义在同名头文件中,和vector
不同,initializer_list
中的元素永远是常量值,无法改变。
列表初始化返回值(c++11)
vector<string> process() {
// expected, actual 是 string 对象
if(expected.empty()) return {};
else if (expected == actual) return {"function", "okay"};
else return {"function", expected, actual};
}
返回数组指针
int (*func(int i))[10]; // func 参数是 int ,返回一个指向大小为 10 的数组的指针
auto func(int i) -> int(*)[10]; // c++11 后等价的写法
constexpr
constexpr
函数指能用于常量表达式的函数,该函数的返回类型及所有形参的类型都是字面值类型,且函数体中必须有且只有一条return
语句
constexpr int new_sz() {return 42;}
constexpr int foo = new_sz();
foo
初始化时,编译器把对constexpr
的调用结果替换成其结果值,且constexpr
函数被隐式的指定为内联函数
内联函数和constexpr
函数可以多次定义,所以可以将其定义在头文件中
assert
assert
是一种预处理宏,当assert(expr)
求值为假时,assert
输出信息并终止程序,否则声明也不做
如果定义了NDEBUG
,则assert
什么也不做,编译时可以使用CC -D NDEBUG main.c
来定义预处理变量
assert
应该仅用于验证那些确实不可能发生的事情
疑惑:
- 习题 6.14 迭代器为何不能作为引用传递