14-2私有继承

私有继承

使用私有继承,基类的公有成员和保护成员都将成为派生类的私有成员。这意味着基类方法不会成为派生类的公有接口的一部分,但可以在派生类的成员函数中使用。

私有继承与包含提供的特性相同,获得实现,但不获得接口。

14.2.1 Student类示例

实现私有继承

私有继承的实现需要用到关键字private,而不是public来定义类。如果不加访问限定符则默认私有继承。

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

使用多个类的继承称为多重继承(MI)。

1.初始化基类组件

因为继承了基类,所以需要用基类的构造函数来初始化基类部分的成员数据,而不是直接访问基类成员来初始化。

Student(const char * str, const double * pd, int n)
: std::string(str), ArrayDb(pd, n) {} //使用类名初始化而不是成员名
2.访问基类的方法

使用包含时用对象名来调用方法,使用私有继承时用类名和解析域运算符来调用方法

double Student::Average() const
{
if(ArrayDb::size()>0) //类名+解析域运算符
return ArrayDb::sum()/ArrayDb::size();
else
return 0;
}
3.访问基类对象

使用私有继承时,方法可以通过类名+解析域运算符来调用,但基类对象本身却没有名字,如何访问?

答案是使用强制类型转换。将派生类转换为基类就可以调用。

const string & Student::Name() const
{
return (const string &) *this; //通过this指针来返回调用方法的对象,再进行强制类型转换
}
4.访问基类的友元函数

用类名显式地限定使用基类函数不适用于友元函数,因为友元不属于类。但可以通过显式地转换成基类来调用正确的函数。

ostream & operator<<(ostream & os, const Student & stu)
{
os<<"Scores for"<<(const string &)stu<<"\n"; //先将对象转换成基类再调用函数
...
}

14.2.2使用包含还是私有继承

正常倾向于使用包含,它易于理解。同时类中有被包含的对象名,方便调用函数。继承则会引起很多问题,尤其是多个基类继承时。

在需要多个同种类对象作为子对象时,只能使用包含,而不能使用继承,因为继承的对象没有名字无法区分彼此。

继承能提供更多特性,一种情况是类包含保护成员,这种成员在派生类中能直接使用,在包含类中却不能直接访问。另一种是需要重新定义虚函数时,只能使用继承的方式来实现,包含却不能做到。此时重定义的函数只能在类中使用,而不是公有的。

综上,通常使用包含来实现

14.2.3保护继承

保护继承是私有继承的变体。关键字:protected

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

当从派生类中派生出另一个类时,私有继承与保护继承之间的区别就体现出来了。

私有继承中,第三代类就不能使用基类的接口,因为基类的成员是二代派生的私有。

保护继承中,第三代类可以使用,因为基类的成员在二代派生中处于保护状态。

(详见P448表14.1)

14.2.4使用using重新定义访问权限

假设要在派生类外使用私有或保护的基类方法,可以这样做:

1.定义一个派生类方法来使用基类方法
double Student::sum() const 
{
return std::valarray<double>::sum();
}
2.将函数调用包含在另一个函数调用中

即使用一个using声明来指出派生类可以使用特定的基类成员

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

上述声明是的valarray<double>::min()valarray<double>::max()可用,使其类似于Student类的公有方法。

注:using声明只使用成员名,没有括号、函数特征标和返回类型。

同时,这使得基类的所有同名函数重载都可用