如果我理解正确,我们至少有两种不同的方法来实现组合。 (为了简单起见,使用智能指针实现的情况除外。我几乎不使用STL而且不想学习它。)
让我们来看看维基百科example:
class Car
{
private:
Carburetor* itsCarb;
public:
Car() {itsCarb=new Carburetor();}
virtual ~Car() {delete itsCarb;}
};
所以,这是一种方式 - 我们有一个指向对象的指针作为私有成员。 人们可以将其重写为:
class Car
{
private:
Carburetor itsCarb;
};
在这种情况下,我们将对象本身作为私有成员。 (顺便说一句,我是否正确地从术语的角度将这个实体称为对象?)
在第二种情况下,隐式调用默认构造函数(如果需要调用非默认构造函数,可以在初始化列表中执行)和析构函数,则不是必须的。但这不是一个大问题......
当然,在某些方面,这两种情况的区别更为明显。例如,在第二种情况下禁止从Car类的const方法调用Carburetor实例的非const方法......
是否有任何“规则”来决定使用哪一个?我错过了什么吗?
答案 0 :(得分:8)
我倾向于选择第一种情况,因为第二种情况要求你在Car.h中#include Carburettor.h。 由于Carburettor是私人会员,因此您不必在其他地方包含其定义,而不是在实际的Car实施代码中。使用Carburettor类显然是一个实现细节,使用Car对象的外部对象不必担心包含其他非强制依赖项。通过使用指针,您只需要在Car.h中使用Carburettor的前向声明。
答案 1 :(得分:8)
在这种情况下,我们将对象本身作为私有成员。 (顺便说一句,将这个实体称为对象我是从术语的角度来写的吗?)
是的,你可以说“该对象”或该类的“实例”。
你也可以谈论包含数据成员“按值”而不是“按指针”(因为“按指针”和“按值”是谈论传递参数的正常方式,因此我希望人们会理解那些术语适用于数据成员)。
是否有任何“规则”来决定使用哪一个?我错过了什么吗?
如果实例由多个容器共享,则每个容器应该通过指针而不是值包含它;例如,如果一个Employee有一个Boss实例,如果几个Employee实例共享同一个Boss,则用指针包含Boss。
如果数据成员的生命周期与容器的生命周期不同,则通过指针包含它:例如,如果数据成员在容器之后实例化,或者在容器之前销毁,或者销毁 - 和 - 在容器的生命周期内重新创建,或者如果数据成员为空则有意义。
另一次必须通过指针(或引用)而不是按值包含数据成员的类型是抽象基类时。
通过指针包含的另一个原因是,这可能允许您在不重新编译容器的情况下更改数据成员的实现。例如,如果Car and Carburetor在两个不同的DLL中定义,您可能希望通过指针包含Carburetor:因为那样您可以通过安装不同的Carburetor.dll
来更改Carburetor的实现,而无需重建{ {1}}。
答案 2 :(得分:3)
组成:尽可能选择成员。需要多态时或使用前向声明时使用指针。当然,如果没有智能指针,使用指针时需要手动内存管理。
答案 3 :(得分:1)
如果Carb的寿命与Car相同,那么非指针形式在我看来更好。如果你必须更换汽车中的Carb,那么我会选择指针版本。
答案 4 :(得分:0)
通常,非指针版本更易于使用和维护。
但在某些情况下,您无法使用它。例如,如果汽车有多个化油器并且您希望将它们放在一个数组中,并且Carburetor构造函数需要一个参数:您需要通过new创建它们,然后将它们存储为指针。