8.7.2 引用成员
对于引用类型的数据成员,同样只能通过成员初始化表达式进行初始化,见代码8.14。
代码8.14 引用数据成员的初始化RefMember
<————————————————文件名:point.h——————————————-> 01 #include<iostream> 02 using namespace std; 03 class point 04 { 05 private: 06 int xPos; 07 int yPos; 08 int&ref1; 09 double&ref2; 10 public: 11 //引用成员的初始化同样要放在初始化表中 12 point(int x,int y,double&z):ref1(xPos),ref2(z) 13 { 14 xPos=x; 15 yPos=y; 16 } 17 //复制构造函数与此一致:引用成员的初始化同样要放在初始化表中 18 point(const point&pt):ref1(pt. ref1),ref2(pt.ref2) 19 { 20 xPos=pt. xPos; 21 yPos=pt. yPos; 22 } 23 void print() 24 { 25 cout<<"xPos:"<<xPos<<",yPos:"<<yPos<<endl; 26 cout<<"ref1:"<<ref1<<",ref2:"<<ref2<<endl; 27 } 28 void SetX(int x) 29 { 30 xPos=x; 31 } 32 }; <——————————文件名:example814.cpp—————————————————> 33 #include"point.h" 34 int main() 35 { 36 double outInt=5. 0; 37 point pt1(3,4,outInt);//有参构造函数 38 pt1. print(); 39 point pt2(pt1);//复制构造函数 40 pt2. print(); 41 cout<<"改变pt1中的x后"<<endl; 42 pt1. SetX(7); 43 pt1. print(); 44 pt2. print(); 45 outInt=6; 46 cout<<"outInt变化后:"<<endl; 47 pt1. print(); 48 pt2. print(); 49 return 0; 50 }
输出结果如下所示。
xPos:3,yPos:4 ref1:3,ref2:5 xPos:3,yPos:4 ref1:3,ref2:5 改变pt1中的x后 xPos:7,yPos:4 ref1:7,ref2:5 xPos:3,yPos:4 ref1:7,ref2:5 outInt变化后: xPos:7,yPos:4 ref1:7,ref2:6 xPos:3,yPos:4 ref1:7,ref2:6
【代码解析】得到了看似奇怪的结果,前面讲过,引用可以看做变量的别名。对类中的引用成员来说,其既可以是类内同类型成员的别名,也可以是外部某个同类型变量的别名。
代码第12行的构造函数“point(int x,int y,double&z):ref1(xPos),ref2(z)”指明:ref1是本对象中成员xPos的别名,而ref2是外部变量的引用,但对复制构造函数来说,情况有所变化,使用“point pt2(pt1);”创建pt2后,pt2的ref1不是pt2.xPos的别名,而是pt1.xPos的别名,pt2.ref2和pt1.ref2一样,都是外部变量outInt的别名,这才有了如上的输出结果。
实际上,C++将类中的引用成员当做一个该类型的指针来对待。在复制构造函数中简单地用“ref1(pt.ref1)”相当于指针的简单赋值,导致两个引用“指向”同一个变量。
将代码8.14中的复制构造函数头改为如下形式,编译运行,体会其中的差异。
point(const point&pt):ref1(xPos),ref2(pt.ref2)
上述代码真正使pt2.ref2成为了pt2.xPos的别名,当然,代码8.14给出的复制构造函数定义等价于默认的复制构造函数,因此,如果类中含有引用类型的数据成员(尤其是引用本对象内的成员时),不要使用默认的复制构造函数,应当显式地给出复制构造函数,避免程序出现无法预料的错误。