实现has-a关系的途径:
- 包含(组合、层次化):类成员本身是另一个类的对象。
- 私有继承。
- 保护继承。
私有继承
在派生时,使用private关键字而不是public来定义类。
class Student:: private string, private valarray<double> {...};
- 使用私有继承,基类的公有成员和保护成员都将称为派生类的私有成员。这意味着基类方法不会成为派生对象公有接口的一部分,但可以在派生类的成员函数中使用它们。
- 使用私有继承时,只能在派生类的方法中使用基类方法。1
- 私有继承中,要想将基类的引用或指针指向派生类,必须进行显式类型转换,将派生类指针或引用转换为基类指针或引用。(见下“私有继承的使用4”)
私有继承的使用
- 继承类的构造函数使用成员初始化列表语法时,私有继承使用类名而不是成员名来标志构造函数:
//包含:
Student(const char * str, const double *pd, int n)
:name(str), scores(pd,n) {}
//私有继承:
Student(const char *str, const double *pd, int n)
: string(str), ArrayDb(pd,n) {}
- 调用基类方法:包含使用对象名来调用基类方法;私有继承使用类名和作用域解析运算符来调用方法。
//包含:
double Student::Average() const
{
return scores.sum()/scores.size();
}
//私有继承:
double Student::Average() const
{
return ArrayDb::sum()/ArrayDb::size();
}
- 访问基类对象:由于私有继承是未被命名的对象,所以需要使用强制类型转换:
const string & Student::Name() const
{
return (const string &) *this;
}
上述方法返回一个引用,指向用于调用该方法的Student对象中的继承而来的string对象。
- 访问基类的友元函数:私有继承中,通过显示转换为基类来调用正确的函数:
ostream & operator<<(ostream & os, const Student & stu)
{
os<<(const string &) stu;
...
}
私有继承与包含:
同:
- 私有继承与包含都只获得实现,不获得接口。
异:
- 包含将对象作为一个命名的成员对象添加到类中;而私有继承将对象作为一个未被命名的继承对象添加到类中。
- 见“私有继承的使用”。
该使用包含还是私有继承:
- 通常使用包含,它容易理解:包含作为显式命名的对象,代码可以通过名称引用对象,而私有继承使得关系更抽象;
- 包含能够包括多个同类的子对象,而私有继承不行。如果某个类需要3个string对象,可以使用包含声明3个独立的string成员,而私有继承中当对象都没有名称时将难以区分;
- 如果新类需要访问原有类的保护成员,必须使用私有继承;
- 如果需要重新定义虚函数,必须使用私有继承。
私有继承与保护继承:
区别主要在从派生类再派生出一另一个类时:
- 使用私有继承,第三代类将不能使用基类的接口。因为基类的共有方法在派生类中将变成私有方法;
- 使用保护继承,第三代类则可以使用基类的接口。因为基类的共有方法在第二代中将变成保护方法,带三代派生类可以使用它们。
想要像公有继承那样直接使用基类方法,见 使用using重新定义访问权限。 ↩︎