现实环境存在各种对象关系,可以使用特殊的关系类型描述词来形容这些关系。
例如:
1. 方形是属于形状的一种,可以用“is-a”来描述方形与形状的关系。
2. 汽车都有方向盘,可以用“has-a”来描述汽车与方向盘的关系。
3. 程序员使用键盘编辑代码,可以用“uses-a”来描述程序员与键盘的关系。
4. 花朵依赖蜜蜂传播花粉,可以使用“depends-on”来描述花朵与蜜蜂的关系。
5. 学生是一个班级的成员,可以使用“member-of”来描述学生与班级的关系。
6. 肢体属于人体的一部分,可以使用“part-of”来描述肢体与人体的关系。
使用C++进行面向对象的编程过程,如果能精准的理解对象间的关系,对编写高质量的代码有很大帮助。
下面列出几个典型的对象关系(非继承):
一. 对象组合关系
对象组合关系易于理解,如一辆车是由车轮、车架、发动机等设备组成的,电脑是由显示器,机箱,CPU等
设备组成的。这些关系称为组合关系(composition),而在这种情况下,整车与轮子的关系为包含(“has-a”)。
在C++中,对象组合关系能够将复杂的类转化为多个简单的类的组合,简化了复杂类的操作。
对象组合又包含两种形式:一种是组合,另一种是聚合。
1. 组合关系:
满足组合关系包含下面几个前提:
a. 成员必须是类的一部分;
b. 成员在一个时期只能属于一个类;
c. 成员在一个类中必须存在;
d. 成员不必知道自身包含在某个类中。
组合的关系尤似人体与心脏的关系,部分必须包含于整体,整体必须由部分组成,某一时刻心脏
必须属于某个人体,而不能属于其他人体。组合关系强调对象的完整性必须由成员来保证,成员与对象是单向
保证的,即对象必须包含成员,而成员不一定存在于此对象。
class Point2D
{
private:
int m_x;
int m_y;
...
public:
...
}
其中m_x、m_y与类Point2D的关系即为组合关系。
2. 聚合关系
满足聚合关系包含下面几个要素:
a. 成员必须是类的一部分;
b. 成员在一个时期可以属于多个类;
c. 成员在类中不必一定存在;
d. 成员不必知道自身包含在某个类中。
同组合关系一样,聚合关系也是部分与整体的关系,但不同于组合关系,成员可以在同一时期属于多个对象,
当聚合关系对象创建或消灭时,聚合成员既不负责创建对象的部件,也不负责消除对象的部件。这好比人与家庭住址的
关系,每个人都有住址,然而这个住址可以属于多个人(父母兄弟)。人员不管理住址,事实上在人住那之前,那个地址
可能就已经存在。此外,人通常知道自己住址名称,但住址不知道谁住在该处。这里人与住址的关系就是聚合关系。
对聚合关系进行建模时,处理对象析构时,整体对象不负责销毁聚合成员。
包含聚合关系的类实现同组合关系基本相同,因为它们都是属于整体部分的关系。
在组合关系中,成员变量通常是普通变量,如果是指针,则组合类必须负责分配和释放该指针指向的空间。
在聚合关系中,成员变量通常是引用或者指针,并且该成员变量引用的是类作用域外的对象。因此,当对象被
销毁时,其中聚合关系成员变量引用或指针将被销毁,但是引用或指向的对象仍然存在。
如下示例,教师与教师名字是组合的关系,教师必须有名字,教师与名字共存,教师类对象销毁后,教师名字也销毁了。
而教师与部门的关系是聚合的关系,该部门可以有很多其他教师,教师也可以同时属于别的部门,该部门销毁后,教师仍然存在。
#include <string> class Teacher { private: std::string m_name; public: Teacher(std::string name) : m_name(name) { } std::string getName() { return m_name; } }; class Department { private: Teacher *m_teacher; // This dept holds only one teacher for simplicity, but it could hold many teachers public: Department(Teacher *teacher=NULL) : m_teacher(teacher) { } }; int main() { // Create a teacher outside the scope of the Department Teacher *teacher = new Teacher("Bob"); // create a teacher { // Create a department and use the constructor parameter to pass // the teacher to it. Department dept(teacher); } // dept goes out of scope here and is destroyed // Teacher still exists here because dept did not delete m_teacher std::cout << teacher->getName() << " still exists!"; delete teacher; return 0; }