复合类型是指基于其他类型定义的类型。接下来介绍c++复合类型中的两种:应用和指针。
1. 引用
1.1 引用即别名
通过将声明符写成&d的形式来定义应用数据类型,其中d是声明的变量名。
int a = 0;//声明基本数据类型
int &refa = a;//声明引用数据类型,refa指向a,是a的另外一个名字
int &refb;//错误,引用必须被初始化
为什么说引用即别名呢?因为定义引用时,初始化完成后,引用将和它的初始值对象一直绑定在一起。简单的一个例子是:
int a = 0;
int b = a;
int &refa = a;
std::cout << "b:" << b << std::endl;
std::cout << "refa:" << refa << std::endl;
b = 1;
std::cout << "change b" << std::endl;
std::cout << "b:" << b << std::endl;
std::cout << "a:" << a << std::endl;
refa = 1;
std::cout << "change refa" << std::endl;
std::cout << "refa:" << refa << std::endl;
std::cout << "a:" << a << std::endl;
运行结果是:
b:0
refa:0
change b
b:1
a:0
change refa
refa:1
a:1
由此我们可以看到,当声明引用refa,并赋值a之后,refa其实就是a的一个别名,改变refa也就是改变a。
由于初始化完成后,引用将和它的初始值对象一直绑定在一起,所以无法令引用重新绑定在另一个对象。
2. 指针
与引用类似,指针也实现了对其他对象的间接访问。然而指针与引用相比有很对不同点。其一,指针本身就是一个对象,允许对指针赋值和拷贝,而且在指针的生命周期内它可以先后指向几个不同的对象。其二,指针无需在定义时赋初值。
2.1 获取对象的地址
定义指针类型的方法将声明符写成*d的形式,其中d是变量名。指针存放某个对象的地址,要想获取该地址,需要使用取地址符(操作符&)。
int i = 1;
int *p = &i;
需要注意的是,因为引用不是对象,只是一个对象的别名,没有实际地址,所以不能定义指向引用的指针。
2.2 指针值
指针的值(即地址)应属下列4种状态之一:
- 指向一个对象
- 指向紧邻对象所占空间的下一个位置
- 空指针,意味着指针没有指向任何对象。
- 无效指针,也就是上述情况之外的其他值。
2.3 利用指针访问对象
如果指针指向了一个对象,则允许使用解引用符(操作符*)来访问该对象。
int i = 1;
int *p = &i;
std::cout << p;//地址
std::cout << *p;//对象
上面的代码会输出:
0x28ff28
1
对指针解引用会得出所指的对象,因此如果给解引用的结果赋值,实际上也就是给指针所指的对象赋值:
int i = 1;
int *p = &i;
std::cout << *p << std::endl;
std::cout << i << std::endl;
*p = 2;
std::cout << *p << std::endl;
std::cout << i << std::endl;
输出为:
1
1
2
2
2.4 符号的多重含义
像&和*这样的符号,既能用作表达式里的运算符,也能作为声明的一部分出现,符号的上下文决定符号的意义:
int i = 1;
int &r = i;//紧随类型名出现,r是一个引用
int *p;//紧随类型名出现,p是一个指针
p = &i;//出现在表达式中,是一个取地址符
*p = i;//出现在表达式中,是一个解引用符
int &r2 = *p;//r2是引用,*是一个解引用符
2.5 赋值和指针
有时候要想搞清楚一条赋值语句到底是改变了指针的值还是改变了指针所指对象的值不太容易,最好的方法就是记住赋值永远改变的是等号左侧的对象。
int i = 1;
int *pi = 0//当前pi没有指向任何对象
pi = &i;//pi的值被改变,现在pi指向了i
*pi = 0;//i的值被改变,指针pi并没有改变