第14章 C++中的代码重用
1.包含对象成员的类
(1)valarray类简介
valarray被定义为一个模板类,以便能够处理不同的数据类型。使用valarray类来声明一个对象时,需要在标识符valarray后面加上一对尖括号,并在其中包含所需的数据类型:
valarray<int> q_values; //an array of int
valarray<double> weights; //an array of double
下面是几个使用其构造函数的例子:
double gpa[5]={3.1,3.5,3.8,2.9,3.3};
valarray<double> v1; //an array of double, size 0
valarray<int> v2(8); //an array of 8 int elements
valarray<int> v3(10,8); //an array of 8 int elements, each set to 10
valarray<double> v4(gpa,4); //an array of 4 elements, initialized to the first 4 elements of gpa
从中可知,可以创建长度为零的空数组、指定长度的空数组、所有元素度被初始化为指定值的数组、用常规数组中的值进行初始化的数组。在C++11中,也可以使用初始化列表:
valarray<int> v5={20,32,17,9}; //C++11
下面是这个类的一些方法:
1)operator[ ]( ):让您能够访问各个元素
2)size():返回包含的元素数
3)sum():返回所有元素的总和
4)max():返回最大的元素
5)min():返回最小的元素
(2)初始化被包含的对象
Student(const char * str,const double * pd, int n)
:name(str),scores(pd,n) {}
因为该构造函数初始化的是成员对象,而不是继承的对象,所以在初始化列表中使用的是成员名,而不是类名;C++要求在构建对象的其他部分之前,先构建继承对象的所有成员对象,如果省略初始化列表,C++将使用成员对象所属类的默认构造函数
(3)使用被包含对象的接口
被包含对象的接口不是公有的,但可以在类方法中使用它。例如,下面的代码说明了如何定义一个返回学生平均分数的函数:
double Student::Average() const
{
if(scores.size()>0)
return scores.sum()/scores.size();
else
return 0;
}
可以定义一个使用string版本的<<运算符的友元函数:
//use string version of operator<<()
ostream & operator<<(ostream & os, const Student & stu)
{
os<<"Scores for"<<stu.name<<":\n";
...
}
同样,该函数也可以使用valarray的<<实现来进行输出,不幸的是没有这样的实现;因此,Student类定义了一个私有辅助方法来处理这种任务:
//private method
ostream & Student::arr_out(ostream & os) const
{
int i;
int lim=scores.size();
if(lim>0)
{
for(i=0;i<lim;i++)
{
os<<scores[i]<<" "";
if(i%5==4)
os<<endl;
}
if(i%5!=0)
os<<endl;
}
else
os<<" empty array";
return os;
}
//use string version of operator<<()
ostream & operator<<(ostream & os, const Student & stu)
{
os<<"Scores for "<<stu.name<<":\n";
stu.arr_out(os); //use private method for scores
return os;
}
辅助函数也可以用作其他用户级输出函数的构建块—如果提供符合条件的函数
2.私有继承
1)包含与私有继承的区别:
包含将对象作为一个命名的成员对象添加到类中,而私有继承将对象作为一个未被命名的继承对象添加到类中(包含版本提供了两个被显式命名的对象成员,而私有继承提供了两个无名称的子对象成员);子对象来表示通过继承或包含添加的对象;对于继承类,使用类名而不是成员名来标识构造函数
包含与私有继承的相同点:获得实现,但不获得接口
2)初始化基类组件
对于构造函数,包含将使用这样的构造函数:
Student(const char * str, const double *pd, int n)
:name(str), scores(pd,n) {} //use object names for containment
对于继承类,新版本的构造函数将使用成员初始化列表语法,它使用类名而不是成员名来标识构造函数:
Student(const char * str, const double * pd, int n)
:std::string(str),ArrayDb(pd,n) {} //use class names for inheritance
3)访问基类的方法
包含使用对象来调用方法:
double Student::Average() const
{
if(scores.size()>0)
return scores.sum()/scores.size();
else
return 0;
}
私有继承使得能够使用类名和作用域解析运算符来调用基类的方法:
double Student::Average() const
{
if(ArrayDb::size()>0)
return ArrayDb::sum()/ArrayDb::size();
else
return 0;
}
4)访问基类对象
例如,Student类的包含版本实现了Name()方法,它返回string对象成员name;但使用私有继承时,该str