一、首先我们要知道c++中类与类之间有哪些关系。
1.继承
继承指的是一个类继承另外的一个类,继承的类叫做子类,被继承的类叫做父类。
语法形式为:
class A{
};
class B:public A{
};
其中A为父类,B为子类,public 是继承方式,具体的内容不再多说,可参考相关的c++书籍。
2.实现
实现讲的是c++中面向对象的“接口”,“接口”是java中的重点,在c++中的接口主要通过纯虚函数实现。
class A{ public: virtual void func(); }; class B:public A{ void func(){ } };类A中的函数func()为纯虚函数,没有函数体,在继承它的函数中写出func()的具体实现。
3.依赖
C++语法中,通过将一个类作为另一类方法的参数的形式实现两个类之间的依赖关系。
class A{ }; class B{ public: void func(A a){}; };
A类型的对象作为B类中函数的参数。
4.聚合
在C++语法中,通过类的指针来实现聚合。
class A{
};
class B{
public:
A a[5];
};
5.关联
C++中,通过定义其他类指针类型的成员来实现关联。
class A{
};
class B{
public:
A *a;
};
6.组合
组合是将一个对象(部分)放到另一个对象里(组合)。相比"聚合",组合是一种强所属关系,组合关系的两个对象往往具有相同的生命周期,被组合的对象是在组合对象创建的同时或者创建之后创建,在组合对象销毁之前销毁。一般来说被组合对象不能脱离组合对象独立存在,而且也只能属于一个组合对象。在C++语法中,使用在一个类中包含另外一个类类型的成员来实现组合。
class A{
};
class B{
public:
A a;
};
二、组合的特殊情况。
class B;
class A{
public:
B b;
};
class B{
public:
A a;
};
第一行的class B 是前置声明,因为B类型还没有定义,要“告诉”编译器 B 是什么。但是,这样编译器就能编译通过了么?
还是不行的。为什么呢?
编译器在编译时是一字一句的编译的,我们来简单看一下他的编译过程:
首先,它“看到”class B,知道了B是一种“类“类型。接着 是A类的定义,在定义A类时,A类中有一个B类型的变量,编译器要知道B类型是什么样的才能继续编译A,只好转去编译B(编译器要为A分配内存就要知道A占用内存空间的大小,也就是要知道B的大小),结果B中也有A的对象而A还没编译完整。编译器知道这样下去只会没完没了,所以它撂挑子不干了,返回一个编译错误就是A没有定义(好像是A,要么就是B,按理应该是A,记不清了),总之会编译错误。
那我们怎么解决这样的问题呢?
class B;
class A{
publc:
B b;
};
class B{
public:
A *a;
};
仔细观察发现将其中一个类的对象改为了指针类型,而指针类型占用的空间是确定的,这样编译器就知道的B的占用空间大小,从而可以编译A。