C++笔记(二)

最近准备完整刷一遍《C++ Primer》,本文是记录的一些小的知识点,包括指向指针的引用,autoconst或引用一起使用,复杂的数组声明,sizeof运算符。

指向指针的引用

1
2
3
4
5
int i = 42;
int *p; // p is a pointer to int
int *&r = p; // r is a reference to the pointer p
r = &i; // r refers to a pointer; assigning &i to r makes p point to i
*r = 0; // dereferencing r yields i, the object to which p points; changes i to 0

要理解r的类型到底是什么,最简单的办法是从右向左阅读r的定义。离变量名最近的符号(此例中是&r的符号&)对变量的类型有最直接的影响,因此r是一个引用。声明符的其余部分用以确定r引用的类型是什么,此例中的符号*说明r引用的是一个指针。最后,声明的基本数据类型部分指出r引用的是一个int指针。

auto与const或引用一起使用

编译器推断出来的auto类型有时候和初始值的类型并不完全一样,编译器会适当地改变结果类型使其更符合初始化规则。

使用引用其实是使用引用的对象,特别是当引用被用作初始值时,真正参与初始化的其实是引用对象的值。此时编译器以引用对象的类型作为auto的类型:

1
2
int i = 0, &r = i;
auto a = r; // a is an int (r is an alias for i, which has type int)

其次,auto一般会忽略掉顶层const,但把底层const保留下来,比如当初始值是一个指向常量的指针时:

1
2
3
4
5
const int ci = i, &cr = ci;
auto b = ci; // b is an int (top-level const in ci is dropped)
auto c = cr; // c is an int (cr is an alias for ci whose const is top-level)
auto d = &i; // d is an int*(& of an int object is int*)
auto e = &ci; // e is const int*(& of a const object is low-level const)

如果希望推断出的auto类型是一个顶层const,需要明确指出:

1
const auto f = ci; // deduced type of ci is int; f has type const int

还可以将引用的类型设为auto,此时原来的初始化规则仍然适用:

1
2
3
auto &g = ci; // g is a const int& that is bound to ci
//auto &h = 42; // error: we can't bind a plain reference to a literal
const auto &j = 42; // ok: we can bind a const reference to a literal

设置一个类型为auto的引用时,初始值中的顶层常量属性仍然保留。如果我们给初始值绑定一个引用,则此时的常量就不是顶层常量了(对常量的引用只能是底层常量,引用不存在顶层常量)。

复杂的数组声明

数组与指针或引用一起使用:

1
2
3
4
5
6
int arr[10];
int *ptrs[10]; // 含有10个整形指针的数组
// int &[10] = ... // 不存在引用的数组
int (*pArray)[10] = &arr; // 指向一个含有10个整数的数组
int (&arrRef)[10] = arr; // 引用一个含有10个整数的数组
int *(&arry)[10] = ptrs; // arry是数组的引用,该数组含有10个指针

从数组的名字开始,按照“由内到外”的顺序来理解数组声明的含义。

多维数组:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
int ia[3][4];
// 大小为3的数组, 每个元素都是含有4个整数的数组
int arr[10][20][30] = {0};
// 大小为10的数组,它的每个元素都是大小为20的数组
// 这些数组的元素是含有30个整数的数组
// 所有元素初始化为 0
int ia2[3][4] = { // 三个元素,每个元素都是大小为4的数组
{0, 1, 2, 3}, // 第一行的初始值
{4, 5, 6, 7}, // 第二行的初始值
{8, 9, 10, 11} // 第三行的初始值
};
// 等价于
// int ia2[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};
ia2[2][3] = arr[0][0][0];
// 使用arr的首元素给ia2的最后一行的最后一个元素赋值
int (&row)[4] = ia[1];
// 把row绑定到ia的第二个4元素数组上

sizeof运算符

sizeof运算符返回一条表达式或一个类型名字所占的字节数。sizeof运算符满足右结合律,所得的值是一个size_t类型。

加入油表达式expr,则使用:

1
sizeof expr ;

来获取表达式expr结果的大小时,并不会实际计算该表达式。

对于解引用的指针类型sizeof *p (等价于 sizeof(*p)),并不会真正地对指针解引用,即使是无效的指针,也同样可以求出大小。

c++11允许使用sizeof配合作用域运算符来获取类的成员并求它的大小。

sizeof运算符的结果部分地依赖于其作用的类型:

  • char或者类型为char的表达式执行sizeof运算,结果得1
  • 对引用类型执行sizeof运算得到被引用对象所占空间的大小。
  • 对指针执行sizeof运算得到指针本身所占空间的大小。
  • 对解引用指针执行sizeof运算得到指针指向的对象所占空间的大小,指针不需有效。
  • 对数组执行sizeof运算得到整个数组所占空间的大小,等价于对数组中所有的元素各执行一次sizeof运算并将所得结果求和。注意,sizeof运算不会把数组转换成指针来处理。
  • string对象或vector对象执行sizeof运算只返回该类型固定部分的大小,不会计算对象中的元素占用了多少空间。

因为执行sizeof运算能得到整个数组的大小,所以可以用数组的大小除以单个元素的大小得到数组中元素的个数:

1
2
3
int arr[10];
// ...
sizeof(arr)/sizeof(*arr) //返回arr的元素数量

REFERENCE

《C++ Primer 第五版》