星球狂想战队|一起来学C++:C++中的代码重用( 五 )


Pleaseenterthestudent'sname:GilBaytsPleaseenter5quizscores:9294969395Pleaseenterthestudent'sname:PatRoonePleaseenter5quizscores:8389727895Pleaseenterthestudent'sname:FleurO’DayPleaseenter5quizscores:9289967464StudentList:GilBaytsPatRooneFleurO'DayResults:ScoresforGilBayts:9294969395average:94ScoresforPatRoone:8389727895average:83.4ScoresforFleurO'Day:9289967464average:83Done.14.2私有继承C++还有另一种实现has-a关系的途径——私有继承 。 使用私有继承 , 基类的公有成员和保护成员都将成为派生类的私有成员 。 这意味着基类方法将不会成为派生对象公有接口的一部分 , 但可以在派生类的成员函数中使用它们 。
下面更深入地探讨接口问题 。 使用公有继承 , 基类的公有方法将成为派生类的公有方法 。 总之 , 派生类将继承基类的接口;这是is-a关系的一部分 。 使用私有继承 , 基类的公有方法将成为派生类的私有方法 。 总之 , 派生类不继承基类的接口 。 正如从被包含对象中看到的 , 这种不完全继承是has-a关系的一部分 。
使用私有继承 , 类将继承实现 。 例如 , 如果从String类派生出Student类 , 后者将有一个String类组件 , 可用于保存字符串 。 另外 , Student方法可以使用String方法来访问String组件 。
包含将对象作为一个命名的成员对象添加到类中 , 而私有继承将对象作为一个未被命名的继承对象添加到类中 。 我们将使用术语子对象(subobject)来表示通过继承或包含添加的对象 。
因此私有继承提供的特性与包含相同:获得实现 , 但不获得接口 。 所以 , 私有继承也可以用来实现has-a关系 。 接下来介绍如何使用私有继承来重新设计Student类 。
14.2.1Student类示例(新版本)要进行私有继承 , 请使用关键字private而不是public来定义类(实际上 , private是默认值 , 因此省略访问限定符也将导致私有继承) 。 Student类应从两个类派生而来 , 因此声明将列出这两个类:
classStudent:privatestd::string,privatestd::valarray{public:...};使用多个基类的继承被称为多重继承(multipleinheritance , MI) 。 通常 , MI尤其是公有MI将导致一些问题 , 必须使用额外的语法规则来解决它们 , 这将在本章后面介绍 。 但在这个示例中 , MI不会导致问题 。
新的Student类不需要私有数据 , 因为两个基类已经提供了所需的所有数据成员 。 包含版本提供了两个被显式命名的对象成员 , 而私有继承提供了两个无名称的子对象成员 。 这是这两种方法的第一个主要区别 。
1.初始化基类组件隐式地继承组件而不是成员对象将影响代码的编写 , 因为再也不能使用name和scores来描述对象了 , 而必须使用用于公有继承的技术 。 例如 , 对于构造函数 , 包含将使这样的构造函数:
Student(constchar*str,constdouble*pd,intn):name(str),scores(pd,n){}//useobjectnamesforcontainment对于继承类 , 新版本的构造函数将使用成员初始化列表语法 , 它使用类名而不是成员名来标识构造函数:
Student(constchar*str,constdouble*pd,intn):std::string(str),ArrayDb(pd,n){}//useclassnamesforinheritance在这里 , ArrayDb是std::valarray的别名 。 成员初始化列表使用std::string(str) , 而不是name(str) 。 这是包含和私有继承之间的第二个主要区别 。
程序清单14.4列出了新的类定义 。 唯一不同的地方是 , 省略了显式对象名称 , 并在内联构造函数中使用了类名 , 而不是成员名 。
程序清单14.4studenti.h
//studenti.h--definingaStudentclassusingprivateinheritance#ifndefSTUDENTC_H_#defineSTUDENTC_H_#include#include#includeclassStudent:privatestd::string,privatestd::valarray{private:typedefstd::valarrayArrayDb;//privatemethodforscoresoutputstd::ostream&arr_out(std::ostream&os)const;public:Student():std::string("NullStudent"),ArrayDb(){}explicitStudent(conststd::string&s):std::string(s),ArrayDb(){}explicitStudent(intn):std::string("Nully"),ArrayDb(n){}Student(conststd::string&s,intn):std::string(s),ArrayDb(n){}Student(conststd::string&s,constArrayDb&a):std::string(s),ArrayDb(a){}Student(constchar*str,constdouble*pd,intn):std::string(str),ArrayDb(pd,n){}~Student(){}doubleAverage()const;double&operator[](inti);doubleoperator[](inti)const;conststd::string&Name()const;//friends//inputfriendstd::istream&operator>>(std::istream&is,Student&stu);//1wordfriendstd::istream&getline(std::istream&is,Student&stu);//1line//outputfriendstd::ostream&operator<2.访问基类的方法使用私有继承时 , 只能在派生类的方法中使用基类的方法 。 但有时候可能希望基类工具是公有的 。 例如 , 在类声明中提出可以使用average()函数 。 和包含一样 , 要实现这样的目的 , 可以在公有Student::average()函数中使用私有Student::Average()函数(参见图14.2) 。 包含使用对象来调用方法: