zl程序教程

您现在的位置是:首页 >  后端

当前栏目

C++类的复合和继承关系(C++继承类和封闭类的关系)

C++继承 关系 复合 封闭
2023-06-13 09:11:55 时间
在 C++ 中,类和类之间有两种基本关系:复合关系和继承关系。

复合关系也称为 has a 关系或 有 的关系,表现为封闭类,即一个类以另一个类的对象作为成员变量。如上节中 CStudent 类的例子,每个 CStudent 对象都 有 一个 string 类的成员变量 name,代表姓名。

继承关系也称为 is a 关系或 是 的关系,即派生类对象也是一个基类对象。如在上节的程序中,CUndergraduateStudent 类(代表本科生)继承了 CStudent 类(代表学生)。因为本科生也是学生,因此可以说,每一个 CUndergraduateStudent 类的对象也是一个 CStudent 类的对象。

在设计两个有关系的类时要注意,并非两个类有共同点,就可以让它们成为继承关系。让类 B 继承类 A,必须满足 类 B 所代表的事物也是类 A 所代表的事物 这个命题从逻辑上是成立的。例如,写一个平面上的点类 CPoint::


class CPoint{

 double x, y; //点的坐标

};

又要写一个圆类 CCircle。CCircle 类有圆心,圆心也是平面上的一点,因而 CCircle 类和 CPoint 类似乎有相同的成员变量。如果因此就让 CCircle 类从 CPoint 类派生而来,即采用如下写法:


class CCircle: public CPoint{

 double radius; //半径

};

是不正确的。因为, 圆也是点 这个命题是不成立的。这个错误不但初学者常犯,甚至很多知名教材也以此作为继承的例子。正确的做法是使用 has a 关系,即在 CCircle 类中引入 CPoint 成员变量,代表圆心:


class CCircle

 CPoint center; //圆心

 double radius; //半径

}

这样,从逻辑上来说,每一个 圆 对象都包含(有)一个 点 对象,这个 点 对象就是圆心 这非常合理。

如果写了一个 CMan 类代表男人,后来发现又需要一个 CWoman 类代表女人,仅仅因为 CWoman 类和 CMan 类有共同之处,就让 CWoman 类从 CMan 类派生而来,同样也是不合理的。因为 一个女人也是一个男人 从逻辑上不成立。

但是让 CWoman 类包含 CMan 类成员对象就更不合适了。

此时正确的做法应该是概括男人和女人的共同特点,编写一个 CHuman 类,代表 人 ,然后 CMan 类和 CWoman 类都从 CHuman 类派生。

有时,复合关系也不一定都是通过封闭类实现的,尤其当类 A 中有类 B,类 B 中又有类 A 的情况。

假设要编写一个小区养狗管理程序,该程序需要一个 主人 类,还需要一个 狗 类。狗是有主人的,主人也有狗。假定狗只有一个主人,但一个主人可以有最多 10 条狗。该如何处理 主人 类和 狗 类的关系呢?下面是一种直观的写法:


class CDog;

class CMaster //主人

 CDog dogs[10];

 int dogNum; //狗的数量

class CDog

 CMaster m;

};

这种写法是无法通过编译的。因为尽管提前对 CDog 类进行了声明,但编译到第 4 行时,编译器还是不知道 CDog 类的对象是什么样的,所以无法编译定义 dog 对象的语句。而且这种 人中有狗,狗中有人 的做法导致了循环定义。

避免循环定义的方法是在一个类中使用另一个类的指针,而不是对象作为成员变量。例如下面的写法:


class CDog;

class CMaster

 CDog* dogs[10];

 int dogNum; //狗的数量

class CDog

 CMaster m;

};

上面这种写法在第 4 行定义了一个 CDog 类的指针数组作为 CMaster 类的成员对象。指针就是地址,大小固定为 4 个字节,所以编译器编译到此时不需要知道 CDog 类是什么样子。

这种写法的思想是:当一个 CMaster 对象养了一条狗时,就用 new 运算符动态分配一个 CDog 类的对象,然后在 dogs 数组中找一个元素,让它指向动态分配的 CDog 对象。

这种写法还是不够好。问题出在 CDog 对象中包含了 CMaster 对象。在多条狗的主人相同的情况下,多个 CDog 对象中的 CMaster 对象都代表同一个主人,这造成了没有必要的冗余:一个主人用一个 CMaster 对象表示足矣,没有必要对应于多个 CMaster 对象。

而且,在一对多这种情况下,当主人的个人信息发生变化时,就需要将与其对应的、位于多个 CDog 对象中的 CMaster 成员变量 m 都找出来修改,这毫无必要,而且非常麻烦。

正确的写法应该是为 狗 类设一个 主人 类的指针成员变量,为 主人 类设一个 狗 类的对象数组。如下所示:


class CMaster;

classCDog

 CMaster* pm;

class CMaster

 CDog dogs[10];

 int dogNum;

};

这样,主人相同的多个 CDog 对象,其 pm 指针都指向同一个 CMaster 对象。

实际上,每个主人未必都养 10 条狗,因此出于节省空间的目的,在 CMaster 类中设置 CDog 类对象的指针数组,而不是对象数组,也是一种好的写法。如下所示:


class CMaster

 CDog* dogs[10];

 int dogNum;

};

有的教材将类 A 的成员变量是类 B 的指针这种情况称为 类 A 知道类 B ,两个类之间是 知道 关系。

21550.html

chtml