8.7.3 类对象成员

    类数据成员也可以是另一个类的对象,比如,一个直线类对象中包含两个point类对象,在直线类对象创建时需要初始化两个point对象,代码8.15中是对直线类和point类的实现。

    代码8.15 类对象成员的初始化ClassMember1


    <————————————文件名:PointAndLine.h——————————————-> 01 #include<iostream> 02 using namespace std; 03 class point//点类的定义 04 { 05 private: 06 int xPos; 07 int yPos; 08 public: 09 point(int x=0,int y=0)//带默认调用的构造函数 10 { 11 cout<<"点的构造函数被执行"<<endl; 12 xPos=x; 13 yPos=y; 14 } 15 point(const point&pt)//复制构造函数 16 { 17 cout<<"点的复制构造函数被执行"<<endl; 18 xPos=pt. xPos; 19 yPos=pt. yPos; 20 } 21 void print() 22 { 23 cout<<"("<<xPos<<","<<yPos<<")"; 24 } 25 }; 26 class line//line类的定义 27 { 28 private: 29 point pt1;//point类对象作为line类成员,此处若写成point pt1(3,4),错 30 point pt2; 31 public: 32 line(int x1,int y1,int x2,int y2):pt1(x1,y1),pt2(x2,y2)//line对象的有参构造函数 33 { 34 cout<<"线的构造函数被执行"<<endl; 35 } 36 line(const line&ll):pt1(ll. pt1),pt2(ll.pt2)//line对象的复制构造函数 37 { 38 cout<<"线的复制构造函数被执行"<<endl; 39 } 40 void draw() 41 { 42 pt1. print(); 43 cout<<"to"; 44 pt2. print(); 45 cout<<endl; 46 } 47 }; <——————————-文件名:example815.cpp————————————————-> 48 #include"PointAndLine.h" 49 int main() 50 { 51 line l1(1,2,3,4);//调用有参构造函数 52 l1. draw(); 53 line l2(l1);//调用复制构造函数 54 l2. draw(); 55 return 0; 56 }

    输出结果如下所示。


    点的构造函数被执行 点的构造函数被执行 线的构造函数被执行 (1,2)to(3,4) 点的复制构造函数被执行 点的复制构造函数被执行 线的复制构造函数被执行 (1,2)to(3,4)

    【代码解析】在line类定义时,和类中的普通变量不能用如“int xPos=0;”初始化一样,在代码第29行,不能使用如“point pt1(3,4);”的形式完成pt1的初始化。point类中的成员xPos和yPos都是private成员。因此,在line类内无法采用诸如“pt1.xPos”的形式对这些成员进行访问,所以,对pt1和pt2的初始化必须放在初始化表达式中进行。

    “line l1(1,2,3,4);”创建了对象l1,其初始化的顺序如下,首先,pt1的构造函数被调用,接着调用pt2的构造函数,最后调用line类的构造函数体。pt1初始化在pt2之前不是因为在成员初始化表中,pt1位于pt2之前,而是因为在line类的定义中,pt1位于pt2之前。初始化表达式中元素初始化的顺序只取决于类定义中各个数据成员的前后关系。复制构造函数同样如此,首先pt1的复制构造函数被调用,接着调用pt2的复制构造函数,最后调用line类的复制构造函数体。

    鉴于point类的一个构造函数为下述形式。


    point(int x=0,int y=0)所以,在line类中重载如下构造函数,都是合法的。 line(int x1,int y1,int x2,int y2):pt1(x1,y1),pt2(x2,y2); line(int x1,int y1):pt1(x1,y1);//pt2无参或全部参数都有默认值的构造函数被隐式调用 line(int x1,int y1):pt2(x1,y1)//pt1无参或全部参数都有默认值的构造函数被隐式调用 line()/pt1和/pt2无参或全部参数都有默认值的构造函数被隐式调用

    如果point类中的数据成员是public(不推荐将数据成员设置为putlic,这违反了信息隐藏原则),可不使用成员初始化表,在l i n e类的构造函数体和复制构造函数体内采用诸如“pt1.xPos=5”的形式初始化pt1,但此时,编译器仍会隐式调用point类的无参构造函数或所有参数都有默认的构造函数,见代码8.16,用以开辟point对象所需要的内存,因此,必须在point类中显式定义一个无参构造函数或所有参数都有默认的构造函数。

    代码8.16 类对象的构造函数隐式调用ClassMember2


    <———————————-文件名:PointAndLine.h———————————————> 01 #include<iostream> 02 using namespace std; 03 class point 04 { 05 public://数据成员xPos和yPos也声明为public,这违法了信息隐藏原则 06 int xPos; 07 int yPos; 08 point(int x=0,int y=0) 09 { 10 cout<<"点的构造函数被调用"<<endl; 11 xPos=x; 12 yPos=y; 13 } 14 void print() 15 { 16 cout<<"("<<xPos<<","<<yPos<<")"; 17 } 18 }; 19 class line 20 { 21 private: 22 point pt1; 23 point pt2; 24 public: 25 line(int x1,int y1,int x2,int y2) 26 { 27 pt1. xPos=x1;//可直接访问pt1和pt2中的公共数据成员xPos和yPos 28 pt1. yPos=y1; 29 pt2. xPos=x2; 30 pt2. yPos=y2; 31 } 32 line(const line&ln)//复制构造函数 33 { 34 pt1. xPos=ln.pt1.xPos; 35 pt1. yPos=ln.pt1.yPos; 36 pt2. xPos=ln.pt2.xPos; 37 pt2. yPos=ln.pt2.yPos; 38 } 39 void draw() 40 { 41 pt1. print(); 42 cout<<"to"; 43 pt2. print(); 44 cout<<endl; 45 } 46 }; <———————————-文件名:example816.cpp———————————————-> 47 #include"point.h" 48 int main() 49 { 50 line l1(1,2,3,4);//调用有参构造函数 51 l1. draw(); 52 line l2(l1);//调用复制构造函数 53 l2. draw(); 54 return 0; 55 }

    输出结果如下所示。


    点的构造函数被调用 点的构造函数被调用 (1,2)to(3,4) 点的构造函数被调用 点的构造函数被调用 (1,2)to(3,4)

    【代码解析】因为point中的数据成员是public的,因此可以在line类的构造函数和复制构造函数中直接对pt1.xPos等赋值,此时,虽然将“line(int x1,int y1,int x2,int y2)”和“line(const line&ln)”之后的数据成员初始化表达式省略,但编译器仍会隐式调用point类的无参构造函数,或者是所有参数都有默认值的构造函数(即代码第8行),用以分配对象成员的内存。

    代码8.16 中的复制构造函数。


    line(const line&ln) { pt1.xPos=ln.pt1.xPos; pt1.yPos=ln.pt1.yPos; pt2.xPos=ln.pt2.xPos; pt2.yPos=ln.pt2.yPos; } 还可以写成如下形式。 line(const line&ln):pt1(ln.pt1.xPos,ln.pt1.yPos),pt2(ln.pt2.xPos,ln.pt2.yPos) { }