再谈C++中的has-a关系(通过复合塑模出has-a、明智而审慎地使用Private继承)

文章《浅析C++中的is-a和has-a关系(公有继承、包含、私有继承)》讲诉了is-a与has-a的区别,并提到了实现has-a关系的两种方法:包含和私有继承。

文章《再谈C++中的is-a关系(确定你的public继承塑模出is-a关系)》讲诉了如何使用公有继承来实现is-a关系。

今天就再谈谈通过复合、私有继承来实现has-a关系。

由于翻译差异,我们姑且认为“包含”和“复合”是一个概念!

一、 使用包含实现has-a关系

这种关系的实现无非就是一个类是另外一个类的成员而已。

valarray类有头文件valarray支持,用于处理数值。
下面我们实现自己的Student类,包含valarray类的成员。

class Student
{
private:
    string name;
    valarray<double> scores;
    ...
};

上诉代码显示:Student类的成员函数可以使用string和valarray类的公有接口来访问和修改name和scores对象。但是在类的外面不允许这样做,只能通过Student类的公有接口访问name和scores.
这就是我们所说的Student获得了其成员对象的实现而没有获得接口。
因此,可以如下使用被包含对象的接口:

double Student::Average() const
{
    if(scores.size() > 0)
        return scores.sum()/scores.size();
    else
        return 0;
}

二、使用私有继承实现has-a关系

c++还有另一种实现has-a关系的途径—-私有继承。使用私有继承,基类的公有成员和保护成员都将成为派生类的私有成员。这意味着基类方法将不会成为派生对象公有接口的一部分,但可以在派生类的成员函数中使用它们。
使用公有继承,基类的公有方法将成为派生类的公有方法。简而言之,派生类将继承基类的接口,这是is-a关系的一部分。使用私有继承,基类的公有方法将成为派生类的私有方法。简而言之,派生类不能继承基类的接口。正如从被包含对象中看到的,这种不完全继承是has-a关系的一部分。
因此私有继承提供的特性与包含相同:获得实现,但不获得接口。所以,私有继承也可以用来实现has-a关系。代码如下:

class Student : private std::string, private std::valarray<double>
{
public:
    ...
};

与使用包含的第一个区别:
包含版本提供了两个被显示命名的对象成员,而私有继承提供了两个无名称的子对象成员。

与使用包含的第二个区别:
私有继承是使用类名而不是成员名来标识构造函数。
包含的构造函数:

Student(const char* str, const double *pd, int n):name(str),scores(pd,n)
{

}

私有继承的构造函数:

Student(const char* str, const double *pd, int n):std::string(str),std::valvarray<double>(pd, n)
{

}

三、使用包含还是私有继承

由于既可以使用包含,也可以使用私有继承来建立has-a关系。大多数c++程序员倾向于前者。

不过私有继承所提供的特性确实比包含多。例如,假设类包含保护成员,则这样的成员在派生类中是可用的,但在继承层次机构外是不可用的。

如果使用组合奖这样的类保护在另一类中,则后者将不是排成类,而是位于继承层次结构之外,因此不能访问保护成员。但通过继承的到的将是派生类,因此他能够访问保护成员。

另一种需要使用私有继承的情况是需要重新定义虚函数。派生类可以重新定义虚函数,但包含类不能。使用私有继承,重新定义的函数将只能在类中使用,而不是公有的。

你可能感兴趣的:(C++)