1. 包含对象成员的类
既然类的数据成员可以是标准类型,数组,结构,那肯定也可以是另外一种类了。最简单的例子就是已经看到的string类来表示这个类的对象的名字了。
string类前面介绍了很多,这章又介绍了一个新类valarray,用于处理数值,支持诸如将数组中所有元素的值相加以及在数组中找出最大和最小的值等操作。
这个类是一个模板类,支持各种数据类型,但声明具体对象的时候必须加上一对尖括号来表示所需的数据类型:
valarray<int> q_values; // an array of int
valarray<double> weights; // an array of double
赋值或者初始化也很简单,用等号就可以
valarray<int> v5 = {20, 32, 17,9};
定义了对象之后就可以用size(), max(), min()等类方法来做很多操作
知道这个类之后,就可以设计一个Student类,然后用一个valarray类的对象valarray<double> scores 来储存Student的成绩。此外这个类还包括一个string的对象表示name
这样就可以声明定义一个构造函数,使用成员初始化列表语法来初始化name和scores成员对象
Student(const char * str, const double * pd, int n) : name(str), scores(pd, n) {}
注意初始化个成员的顺序必须是它们被声明的顺序。
如果要使用包含类的方法也很简单,比如socres.size() 就是调用valarray类的方法成员size()。
2. 私有继承
私有继承是实现has-a关系的一种重要途径。比如上节的Student类也可以看成String和Valarray的子类:
class Student : private string, private valarray<double>
{ ...}
先不管Student能同时继承String 和valarray. 首先新的Student不续约name和scores两个私有数据,因为两个基类已经提供了所需的所有数据成员,即此时私有继承提供了两个无名称的子对象成员Student has a string and a valarray.
这个时候新版本的构造函数使用类名来标识构造函数:
Student(const char * str, const double * pd, int n) : string(str), valarray<double>(pd, n) {}
要使用string或者valarray的类方法(访问基类方法)时使用类名和作用域解析运算符来调用基类的方法:
valarray<double> :: size()
valarray<double> :: sum()
那么要使用基类对象本身时怎么办呢,答案是使用强制类型转换,如果把Student转换成string,则可以返回名字,如果转换成valarray<double>,则可以访问分数,这个原则同样适用于友元函数.
现在有两种定义Student类的方法了,一个是包含继承,一个是has a继承。一般来说大家倾向于前者,应为好理解,但私有继承(has a)继承提供的特性较多,比如可以访问保护成员。这个还得在后面的学习过程中慢慢体会
除了私有继承之外还有保护继承,使用关键字protected。使用保护继承是,基类的工业成员和保护成员都将成为派生类的保护成员。
当从派生类派生出另一个类时,私有继承第三代类将不能使用基类的结构,因为基类的公有方法在派生类中将变成私有方法,使用保护继承时,基类的公有方法在第二代中将变成受保护的,因此第三代派生类可以使用它们
其实现在学习了三种继承方式,但具体区别还不是很清楚。。。。还得慢慢体会 :(