依赖、关联、聚合、组合、实现、继承 6种关系
各种关系的强弱顺序:
继承 = 实现 > 组合 > 聚合 > 关联 > 依赖
1.依赖
A类使用了B类的一部分属性和方法,不会主动改变B类中的内容,是一种临时性的关联。
- 类A把类B的实例作为方法里的参数使用
- 类A的某个方法里使用了类B的实例作为局部变量
- 类A调用了类B的静态方法
2.关联
关联是一种弱所属关系,但并不是从属关系,类之间是平等的。关联可以有方向,可以是单向关联,也可以是双向关联。
C++中,通过定义其他类指针类型的成员来实现关联。
class A{
B *b;
};
class B{
public:
A *a;
};
3.聚合
聚合也是一种弱所属关系,部分与整体之间没有相同的生命周期,整体消亡后部分可以独立存在。就像汽车和司机的关系。代码实现时,整体类中传入一个部分类的指针,部分类已经在整体类外被构造,因而在整体类析构的时候,部分类并没有被析构。是一种松散的整体和局部的关系(has-a)
class A{
};
class B{
public:
A a[5];
};
关联和聚合的区别主要在语义上,关联的两个对象之间一般是平等的,例如你是我的朋友,聚合则一般不是平等的,例如一个公司包含了很多员工,其实现上是差不多的。
4.组合
组合是将一个对象(部分)放到另一个对象里(组合)。它是一种 "contains-a" 的关系。相比"聚合",组合是一种强所属关系,组合关系的两个对象往往具有相同的生命周期,被组合的对象是在组合对象创建的同时或者创建之后在组合类内部创建,在组合对象销毁之前销毁。一般来说被组合对象不能脱离组合对象独立存在,而且也只能属于一个组合对象。
class A{
};
class B{
public:
A a;
};
特殊情况
class B;
class A{
public:
B b;
};
class B{
public:
A a;
};
class B 是前置声明,因为B类型还没有定义,但还是没法编译通过的。需要进一步改造
class B;
class A{
publc:
B b;
};
class B{
public:
A *a;
};
另外:
class A
{
private:
B* p_b;
}
A::A()
{
p_b=new B;
}
A::~A()
{
delete p_b;
}
A与B的关系是组合。虽然是 用指针表示的,依然是组合关系。
到底是组合还是聚合,其实现形式(用成员还是指针)不是判断标准,两者生命周期的差异,才是判断标准。两者生命周期相同的,是组合,比如成员变量,你这里用指针的实现,而生命周期不同的,才是聚合。
5.实现
实现对应的是面向对象中的"接口"。
在C++中,接口通过的纯虚函数来实现,C++的多态就是通过虚函数来实现的。
class A{
public:
virtual void func() = 0;
};
class B:public A{
void func(){
}
};
延伸:
,C++的关键字中并没有interface,但java和C#中有interface关键字,即接口。interface和class不同,interface仅有接口声明,而且所有的声明默认的访问权限是public而非private(是不是想到了C++中的struct?)。
对于C++来说,这相当于抽象类的概念,即其中的成员函数都是纯虚函数,只有声明,没有实现。如:
class abstractClass{
virtual void memfunc1() = 0;
virtual void memfucn2() = 0;
};
这是一个用于实现接口的纯抽象类,仅包括纯虚函数的类(一般用作基类,派生类进行具体的实现)。纯虚函数是指用=0标记的虚函数。
抽象类是不能实例化的,换句话说,它只是提供一个interface的功能,它并不实现这些纯虚函数。
6.继承
继承是面向对象的三大特征之一,是一种最能体现面向对象代码复用的类关系,对于继承,可以使用"is-a"来表示
class A{
};
class B:public A{
};
从生命周期来看,关联时大家的生命周期大家都管不着,聚合时部分的生命周期长于整体,组合时整体与部分共存亡。
继承表达的是一种上下级的关系(is-a),聚合表达的是一种松散的整体和局部的关系(has-a),而组合表达的是一种紧密的整体局部关系(contain-a)
关联、聚合、组合只能配合语义,结合上下文才能够判断出来,而只给出一段代码让我们判断是关联,聚合,还是组合关系,则是无法判断的。
对c++程序而言,组合关系的语义可以用按值包含的语法实现。聚合或关联都时用按指针包含的语