“C++ Primer”


变量声明和定义

const

数组与指针

  1. 数组指针

    1
    2
    
    int (*Parray)[10] = &arr;	// Parray 指向一个含有 10 个整数的数组
    int (&arrRef)[10] = arr;	// arrRef 引用一个含有 10 个整数的数组
    
  2. 指针数组

    1
    
    int *ptrs[10];	// 含有 3 个整数的数组
    

beginend (11)

1
2
3
int ia[] = {0,1,2,3};
int *beg = begin(ia);	// 指向 ia 首元素的指针
int *last = end(ia);	// 指向 ia 尾元素的下一位置的指针,这两个函数定义在 iterator 头文件中

左值

  1. 可以出现在 operator=左边

  2. 当对象被用作左值的时候,用的是对象的身份(在内存中的位置)

右值:

  1. 可以出现在 operator=右边
  2. 当一个对象被用作右值的时候,用的是对象的值(内容)

  3. 如果表达式的求值结果是左值,decltype作用于该表达式(不是变量)得到一个引用类型

    1
    2
    3
    
    int *p = nullptr;
    decltype(*p) x;	// 解应用得到的结果是左值,所以 x 是 int&
    decltype(&p) y;	// 取地址运算符生成右值,所以 y 是 int**
    

赋值运算符

  1. 赋值运算符的结果是它的左侧运算对象,并且是一个左值

  2. 如果赋值运算符的左右两个运算对象类型不同,则右侧运算对象将转换成左侧运算对象的类型

  3. 赋值运算符满足右结合律

    1
    2
    
    int ival, jval;
    ival = jval = 1; // 正确
    

递增和递减运算符

成员访问运算符

点运算符与箭头运算符的关系:ptr->mem 等价于 (*ptr).mem

sizeof

sizeof是一个运算符而不是一个函数,返回一条表达式或者一个类型名字所占的字节数,返回值为 size_t类型。

1
2
3
4
5
6
7
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)

强制类型转换

  1. static_cast任何具有明确定义的类型转换,只要不包含底层 const 都可以使用

    1
    
    double slope = static_cast<double>(j) / i;
    
  2. 
    ```c++
    const char *pc;
    char *p = const_cast<char *>(pc); // 去掉了 const 属性
    
  3. dynamic_cast

    用于动态类型转换。只能用于含有虚函数的类,用于类层次间的向上和向下转化。只能转指针或引用。 向上转换:指的是子类向基类的转换 向下转换:指的是基类向子类的转换

范围for

1
2
for (declaration : expression)
  statement

expression 表示的必须是一个序列,如花括号括起来的初始值列表、数组、vector string 等,这些类型的共同特点是拥有能返回迭代器的beginend成员

局部静态对象:将局部变量定义成 static 类型

局部静态对象在程序执行路径第一次经过对象定义语句时初始化,并且知道程序终止时才被销毁,在此期间对象所在的函数结束执行也不会对它有影响。

函数声明

类似于变量,函数只能定义一次,但是可以声明多次

initializer_list

一种标准库类型,用于表示某种特定类型的值的数组,定义在同名头文件中,和vector不同,initializer_list中的元素永远是常量值,无法改变。

列表初始化返回值(c++11)

1
2
3
4
5
6
vector<string> process() {
  // expected, actual 是 string 对象
  if(expected.empty())	return {};
  else if (expected == actual) return {"function", "okay"};
  else return {"function", expected, actual};
}

返回数组指针

1
2
int (*func(int i))[10];	// func 参数是 int ,返回一个指向大小为 10 的数组的指针
auto func(int i) -> int(*)[10]; 	// c++11 后等价的写法

constexpr


```c++
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应该仅用于验证那些确实不可能发生的事情

疑惑:

  1. 习题 6.14 迭代器为何不能作为引用传递