C++复合类型(1)
复合类型(compound type)是指基于其他类型定义的类型。C++语言有几种复合类型,这里将介绍两种复合类型引用和指针。
与我们已经掌握的变量声明相比,定义复合类型的变量要复杂很多。一条简单的声明语句由一个数据类型和紧随其后的一个变量名列表组成。其实更通用的表述是,一条声明语句由一个基本数据类型(base type)和紧随其后的声明符(declarator)列表组成。每个声明符命名了一个变量并指定该变量与基本数据类型有关的某种类型。
目前为止,我们所接触的声明语句中,声明符其实就是变量名,此时变量的类型也就是声明的基本数据类型。其实还可能有更复杂的声明符,它基于基本数据类型得到的更复杂的类型,并把它指定给变量。
引用
引用(reference)是C++对C语言的重要扩充。引用就是某种目标变量的“别名”(alias),对引用的操作与对变量直接操作的效果相同。申明一个引用的时候,切记要对其初始化。引用声明完毕后,相当于目标变量名有两个名称,即该目标原名和引用名,不能再把该引用名作为其他变量的别名,他本省不是一种数据类型,因此引用本省不占存储单元,系统也不给引用分配存储单元。不能建立数组的引用。
引用的声明格式:
引用类型名 & 引用名 = 被引用的变量;
被引用的变量应该是已声明或已定义的变量。
引用既然是一个变量的别名,那么,两者的数据类型必须相同,而且在声明引用时必须同时初始化(除引用是用做函数的参数或者是返回值),说明它是哪一种变量的引用。
例如:
int a;
int &ra = a; // ra 指向 a (是 a 的另外一个名字)
int &ra2; // 报错:引用必须初始化
一般在初始化变量时,初始值会被拷贝到新建的对象中。然而定义引用时,程序把引用和他的初始值绑定(bind)在一起,而不是将初始值拷贝给引用。一旦初始化完成,引用将和它的初始值对象一直绑定在一起。因为无法令引用重新绑定到另外一个对象,因此引用必须初始化。
引用并非对象,相反的,他只是为一个已经存在的对象所起的另外一个名字
引用的定义
允许在一条语句中定义多个引用,其中每个引用标识符都必须以符号&开头:
int i = 1024, i2 = 2048; // i和 i2 都是 int
int &r = i, r2 = i2; // r 是一个引用,与 i 绑定在一起,r2 是 int
int i3 = 1024, &ri = i3; // i3 是int ,ri 是一个引用,与 i3 绑定在一起
int &r3 = i3, &r4 = i2; // r3 和 r4 都是引用
/* 错误示例 */
int &a = 10; // 错误:引用类型的初始值必须是一个对象
double a = 3.14;
int &ai = a; // 错误:此处引用类型的初始值必须是 int 型对象
使用引用的注意事项
- 声明引用时,如果不同时初始化以指明它与哪个已声明(或定义)的变量相关联,则会出现编译错误。
int a = 10, b = 20;
int &ra; // 错误,引用 ra 未被初始化
int &ra = b; // 正确
- 引用声明的字符“ & ”并不是地址运算符,它只是用来声明引用,除此之外,其他的应用都属于地址运算符。
int a = 20;
int &ra = a; // ra 是 a 的引用,& 是引用的声明符
int *p;
p = &a; // & 是地址运算符
- 引用一旦被初始化,将它与某个变量关联起来后,就不会再分开。与此不同,指针在赋予某变量地址后,可以指向其他变量。
int a = 20, b = 30;
int &ra = a; // 指定引用 ra 与 a 关联,ra = 20
···
ra = b; // ra 仍与 a 关联,此时,ra = 30,a = 30
- 不允许对 void 类型进行引用。
void a = 10;
void &ra = a; // 错误
因为 void 只是语法上的一个类型,不能建立一个类型为 void 的变量。因此不能生命对 a 的引用。
- 不能建立数组的引用。
int array[s];
int &ra = array; // 错误,array 只是 array[s] 的起始变量
- 与指针不同,引用本身不是变量,指针是变量。
- 指针可以有引用。
int * p;
int *&rp = p; // rp 是指针 p 的引用
int b = 10;
rp = &b; // 正确,rp 是指针 p 的别名,指向变量 b
- 指针可以作为数组的元素,引用不可以做为数组的元素。
引用作为函数参数
将“引用”作为函数参数的特点:
- 传递引用给函数与传递指针的作用效果是一样的。这时,这时被调函数的形参就成为原来主调函数的实参或者对象的一个别名来使用,所以在被调函数中对形参的操作就是对其相应的目标对象的操作。
- 使用引用传递函数的参数,在内存中并没有实参的副本,它是实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配内存单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。
例子:用引用作为参数,交换两个数
#include <iostream>
using namespace std;
void swap(int & x, int & y){
int t;
t = x;
x = y;
y = t;
}
void main(){
int a = 1, b = 2;
cout << "交换前:a = " << a << ",b = " << b << endl;
swap(a, b);
cout << "交换后:a = " << a << ",b = " << b << endl;
}
程序执行结果:
交换前: a = 1,b = 2
交换后: a = 2,b = 1
函数中的形参是主调函数中实参的引用。在函数中改变了形参,就是改变了实参,因此交换功能实现。
例子:用一般变量作为参数,交换两个数
#include <iostream>
using namespace std;
void swap(int x, int y){
int t;
t = x;
x = y;
y = t;
}
void main(){
int a = 1, b = 2;
cout << "交换前:a = " << a << ",b = " << b << endl;
swap(a, b);
cout << "交换后:a = " << a << ",b = " << b << endl;
}
程序执行结果:
交换前: a = 1,b = 2
交换后: a = 1,b = 2
主函数的变量 a 和 b 的值(副本)作为实参进栈。采用传值方式时,实参不受影响。
引用作为函数返回值
以引用作为函数的返回值是为了使函数可以作为左值而被赋值。函数的返回值为引用类型表示表示该函数的返回值是一个内存变量的别名。函数调用既可以作为一个变量来使用,也可以为其赋值。
例子:
/* 求两个数中最小值 */
#include<iostream>
using namespace std;
int & min(int & i, int & j){
if (i <= j)
return i;
else
return j;
}
void main(){
int a = 3, b = 4;
cout << "a = " << a << " b = " << b << endl;
min(a, b) = 5;
cout << "a = " << a << " b = " << b << endl;
min(a, b) = 0;
cout << "a = " << a << " b = " << b << endl;
}
程序执行结果:
a = 3 b = 4
a = 5 b = 4
a = 5 b = 0
注意事项:
- 不能返回局部变量的引用
- 不能返回函数内部 new 分配的内存的引用
- 可以返回类成员的引用,但最好是 const
- 流操作符重载返回值申明为“引用”的作用:流操作 << 和 >> ,这两个操作符常常希望被连续使用
- 在另外的一些操作符中,千万不能返回引用:+、—、*、/ 、四则运算符,它们必须构造一个对象作为返回值