组合

在面向对象程序设计中,可以对复杂对象进行分解、抽象,把一个复杂对象分解为简单对象的组合,由比较容易理解和实现的部件对象装配而成。

实际上类的成员数据既可以是基本类型也可以是自定义类型,当然也可以是类的对象。

类的组合描述的就是一个类内嵌其他类的对象作为成员的情况,他们之间的关系是一种包含与被包含的关系。

当创建类的对象时,如果这个类具有内嵌对象成员,那么各个内嵌对象将首先被自动创建。因此,在创建对象时既要对本类的基本类型数据成员进行初始化,又要对内嵌对象成员进行初始化。这时,理解这些对象的构造函数被调用的顺序就很重要了。

组合类构造函数定义的一般形式为:

类名::类名(形参表):内嵌对象1(形参表),内嵌对象2(形参表),...

{类的初始化}

其中,内嵌对象1(形参表),内嵌对象2(形参表),...称作初始化列表,其作用是对内嵌对象进行初始化。

对基本类型的数据成员也可以这样初始化,而且使用初始化列表比使用赋值语句的效率要高。如

Circle::Circle(float r):radius(r){}

在创建一个组合类的对象时,不仅它自身的构造函数将被调用,而且还将调用内嵌对象的构造函数。这时构造函数的调用顺序是:

a、调用内嵌对象的构造函数,调用顺序按照内嵌对象在组合类的定义中出现的次序。

b、执行本类构造函数的函数体。

如果声明组合类的对象时没有指定对象的初始值,则默认形式(无形参)的构造函数被调用,这时内嵌对象的默认形式构造函数也被调用。析构函数的调用执行顺序与构造函数刚好相反。

对于一个类,如果没有编写拷贝拷贝构造函数,编译系统会在必要时自动生成一个默认的拷贝构造函数。若建立组合类的对象时调用默认拷贝构造函数,则编译器将自动调用内嵌成员对象的拷贝构造函数。

如果要为组合类编写拷贝构造函数,则需要为内嵌成员对象的拷贝构造函数传递参数,如假设C类中包含B类的对象b作为成员,C类的拷贝构造函数形式如下:

C::C(C &c1):b(c1.b)

{...}

例子:

类的组合,线段类,由两个Point类的对象来表示端点,使用类line包括Point类的两个对象p1和p2作为其成员,Line具有计算其长度的功能,在构造函数中实现。

#include<iostream>
#include<cmath>
using namespace std;
class Point
{
public:
Point(int xx=0,int yy=0){X=xx;Y=yy;}
Point(Point &p);
int GetX(){return X;}
int GetY(){return Y;}
private:
int X,Y;
};
Point::Point(Point &p)
{
X=p.X;
Y=p.Y;
cout<<"Point 拷贝构造函数被调用"<<endl;
}
class Line
{
public:
Line(Point xp1,Point xp2);
Line(Line &);
double GetLen(){return len;}
private:
Point p1,p2;
double len;
};
Line::Line(Point xp1,Point xp2):p1(xp1),p2(xp2)
{
cout<<"Line 构造函数被调用"<<endl;
double x=double(p1.GetX()-p2.GetX());
double y=double(p1.GetY()-p2.GetY());
len=sqrt(x*x+y*y);
}
Line::Line(Line &L):p1(L.p1),p2(L.p2)
{
cout<<"Line拷贝构造函数被调用"<<endl;
len = L.len;
}int main()
{
Point myp1(1,1),myp2(4,5);
Line line(myp1,myp2);
Line line2(line);
cout<<"The length of the line is:";
cout<<line.GetLen()<<endl;
cout<<"The length of the line2 is :";
cout<<line2.GetLen()<<endl;
}

2、前向引用声明

考虑到类的组合中,很可能遇到两个类相互引用的情况,这种情况也称为循环依赖,因为不管把谁放在前面都会存在未定义先使用的情况,故引入了前向引用,它是在引用未定义的类之前,将该类的名字告诉编译器,使编译器知道那是一个类名。这样,当程序中使用这个类名时,编译器就不会认为是错误,而类的完整定义可以在程序的其他地方。

class B;//前向引用声明
class A
{
  public:
    void f(B b);//以B类对象b为形参的成员函数
};
class B
{
  public:
    void g(A a);//以A类对象a为形参的成员函数
}

应该记住,当使用前向引用声明时,只能使用被声明的符号,而不能涉及类的任何细节。