C++类的组合和聚合
一、C++重用类代码
“程序=算法+数据”。程序中的数据包括原始数据、中间结果和最终结果。C++语言通过定义变量语句来申请内存空间,定义变量语句是与数据相关的代码,即数据代码。C++通过定义函数来描述算法模块,函数是与算法相关的代码,即算法代码。
在面向对象的程序设计中,类是重用“数据代码+算法代码”的基本语法形式。C++重用类代码有三种方式,分别是用类定义对象、通过组合来定义新类或通过继承来定义新类。目前我们常用的类定义对象就是一种重用类代码的方式。
为了更好说明类代码的重用方法,下面我们定义一个Circle圆形类:
//圆形类声明 class Circle { private: double r; //半径 public: void Input(); //输入半径 double Radius(); //读取半径 double CArea(); //求圆形面积 double CLen(); //求圆形周长 Circle() //无参构造函数 { r = 0; } Circle(double x) //有参构造函数 { if(x<0) r = 0; else r = x; } Circle(Circle &x) //拷贝构造函数 { r = x.r; } }; //类实现 void Circle::Input() { cin >> r; while( r < 0 ) cin >> r; } double Circle::Radius() { return r; } double Circle::CArea() { return (3.14*r*r); } double Circle::CLen() { return (3.14*2*r); }
二、类的组合
1、组合类的定义
程序员可以将别人编写的类当作零件(零件类),在此基础上定义自己的新类(整体类),这就是类的组合。组合的编程原理是:程序员在定义新类的时候,使用已有的类来定义数据成员。这些数据成员是类类型的对象,被称为对象成员。C++语言将数据成员中包含对象成员的类称为组合类。按照数据类型的不同组合类中的数据成员可分为对象成员和基本数据类型的非对象成员。
不同数据成员的访问方式:
1)非对象成员: 组合类对象名.非对象成员名
2)访问对象成员的下级成员: 组合类对象名.对象成员名.对象成员的下级成员名
注意:多级访问将受到多级权限的控制
举例,现在我们又一个蓄水池,该蓄水池是由三个半径大小不同的圆形水池组成的,那么我们将该蓄水池抽象成一个类。
下面为蓄水池类的定义:
//蓄水池类声明 class TriCircle { public: Circle c0,c1,c2; //三个半径不同的圆形水池 double TArea(); //求蓄水池总面积 double TLen(); //求蓄水池总周长 }; //类实现 double TriCircle::TArea() { double totalArea; totalArea = c0.CArea() + c1.CArea() + c2.CArea(); return totalArea; } double TriCircle::TLen() { double totalLen; totalLen = c0.CLen() + c1.CLen() + c2.CLen(); return totalLen; }
可以注意到蓄水池类TriCircle中包含三个Circle类对象成员,分别代表着三个半径不同的圆形水池,此时就是一种类代码的重用实现。在组合类中访问对象成员通常是在访问其下级成员,访问时受下级成员额访问权限控制。对象成员中,只有公有权限的下级成员才能被访问。如Circle中的CArea和CLen公有成员。
在组合类中可以对对象成员进心二次封装,如果将TriCircle中的c0,c1,c2三个对象成员声明成私有成员,那么该组合类TriCircle的对象成员的下级成员都将被隐藏起来,即便是Circle中的公有成员也会被隐藏,此为多级权限控制。
2、组合类对象的定义和访问
与不同类一样,可以使用组合类定义对象。语法形式为:组合类名 对象名;如:TirCircle obj;(类的初始化暂未考虑) 一个组合类对象所占用的内存空间等于类中全部数据成员(含对象成员)所需内存空间的总和。
组合类对象中对象成员的多级访问语法形式:
组合类对象名 . 对象成员名 . 对象成员的下级成员名 例如:obj.c0.Input();
组合类对象指针名 -> 对象成员名 . 对象成员的下级成员名 例如:TriCircle *p= &obj; p->c0.Input();
通过对象指针可以间接访问组合类对象及其下级成员。
3、多级组合
组合类可以任意多级。用零件类定义组合类,组合类可以继续作为零件类去定义更大的零件类,这就是多级组合。多级组合过程中,每一级组合类都很根据自己的功能需要设定对象成员的访问权限,这就参数了多级封装。
4、组合类对象的构造与析构
组合类构造函数不能直接初始化类中的对象成员,因为对象成员的下级数据成员可能是私有的,不能访问赋值。要想初始化对象成员,必须通过其所属类的构造函数才能完成。调用对象成员所属类构造函数,其语法形式是在组合类构造函数的函数头后面添加初始化列表:
组合类构造函数名(形参列表):对象成员名1(形参1),对象成员名2(形参2),...
{
...;//在函数体中初始化其它非对象成员
}
形参1,2是从形参列表中提取出来的,并在初始化列表进行二次接力传递。组合类对象中各数据成员的初始化顺序是:先调用对象成员所属类的构造函数,初始化对象成员;在执行组合类构造函数的函数体,初始化其他非对象成员。对象成员的初始顺序由其在组合类中的声明顺序决定,先声明者先初始化。
1)有参构造函数
TriCircle::TriCircle(double p0,double p1,double p2):c0(p0),c1(p1),c2(p2) {}
示例: TriCircle obj(5,2,3); //按照实参-形参匹配原则调用有参构造函数,将对象成员c0、c1、c2的半径分别初始化为5,2,3。
初始化列表初始化对象成员c0、c1、c2。因为类TriCircle的数据成员中没有分对象成员,所以函数体为空。
2)无参构造函数
TriCircle::TriCircle() {}
示例:TriCircle obj1; //按照实参-形参匹配原则调用无参构造函数,虽然无参构造函数没有初始化列表,但仍然会自动调用Circle类队形的无参构造函数将对象成员c0、c1、c2的半径初始化为0。
3)拷贝构造函数
TriCircle::TriCircle(TriCircle &rObj ):c0(rObj.c0),c1(rObj.c1),c2(rObj.c2) {}
示例:TriCircle obj2(obj);按照实参-形参匹配原则调用拷贝构造函数,初始化列表将形参rObj所引用的实参obj中的三个对象成员c0、c1、c2接力传递给Circle类拷贝构造函数,分别复制到obj2中的对应成员。
4)组合类的析构函数
组合类对象中数据成员的析构顺序是:执行组合类析构函数的函数体,清理非对象成员;在自动调用对象成员所属类的析构函数,清理对象成员。可以注意:组合类对象的析构顺序与构造顺序相反,即先析构非对象成员,在析构对象成员。
三、类的聚合
C++语言将数据成员中包含对象成员的类称为组合类,而将数据成员中包含对象指针的类称为聚合类。聚合类是一种特殊形式的组合类。
聚合类和组合类的区别:
1)聚合类的对象成员是独立创建的,聚合类对象只包含指向对象成员的指针。
2)聚合类对象可以共用对象成员。
示例:
class pTriCircle
{
public:
Circle *p0,*p1,*p2; //对象指针
double TArea(); //求蓄水池总面积
double TLen(); //求蓄水池总周长
};