一个类对象,可以是另一个类的成员。就像string类可以作为其他类的类成员一样。
valarray类:
valarray类是一个模板类,模板类可以处理不同的数据类型。
其头文件是:<valarray>
使用和声明方式类似vector类和array类。
其构造函数有以下几种:
①valarray<int>abc; //声明一个int类型的valarray类对象(是一个int数组),其长度为0
②valarray<double>abc(5); //声明一个长度为5的double数组(即有5个元素)
③valarray<double>abc(3,5); //声明一个有五个成员的double数组,并且把每个成员的值都初始化为3
④ int a[2] = { 1,2 };
valarray<int>abc(a, 4);
//声明一个int数组,有4个成员,并且将前2个成员初始化为数组a的值(即第一个成员值为1,第二个成员值为2)
⑤valarray<int>abc={1,2,3,4}; //声明一个int数组,有四个成员,其值分别被初始化为1,2,3,4。——这是C++11的初始化列表
类方法:
①operator[](); 访问各个元素
例如:cout << abc.operator[](1) << endl;就是输出abc数组的第二个元素。也可以简单的写为abc[1]
②size(); 返回包含的元素数
③sum(); 返回所有元素的总和
④max(); 返回最大值
⑤min(); 返回最小值
建立has-a关系:
如:
class Student
{
private:
string name;
valarray<double>scores;
...
}
就是在Student类中包含了string类和valarray类对象。
Student类具有的特点为:
①可以使用string类和valarray类提供的公有接口;
②Student类本身并不继承这些公有接口。例如,string类对象之间可以相加,而Student类除非定义了operator+()方法,否则是不能相加的;
③不继承接口的has-a关系的组成部分。
注意:
①在类声明中使用的是valarray<double>scores; 因此,若在构造函数开始之前初始化它,那么它就是一个double类型,长度为0的数组(因为调用valarray类的默认构造函数);
②因此,如果想要创建例如一个长度为2的数组,应该在构造函数之前,使用列表初始化的方式进行初始化,例如:Student(int n):scores(n) {...Student的构造函数...}。其效果相当于valarray<double>scores(n) (可参见之前的构造函数②)
③之所以如此,是因为声明一个类时,在进入类的构造函数之前,是可以进行列表初始化的;进入构造函数之后,则各个数据成员已经被初始化了(对于类对象而言,是已经调用了其默认构造函数)。
初始化顺序:
在初始化列表包含多个项目时,其初始化顺序为他们被声明的顺序,而不是在初始化列表中的顺序。例如若在Student类的构造函数中使用初始化列表,无论顺序如何,必将先初始化name,再初始化scores。
特别是在代码使用一个成员的值作为另一个成员初始化列表表达式的一部分时,初始化顺序很重要。例如加入第三个类成员,其需要使用name成员作为其初始化的参数,那么name在Student类的顺序就必须位于该类成员之前。
因此有类定义:
class Student { typedef valarray<double> arr; //将arr作为其别名 string name; arr scores; ostream& scores_out(ostream& os); //输出分数,我怀疑可以使用void作为返回值应该也可以。 public: Student() :name("No Student"), scores() {} //默认构造函数,名字为No Student,分数无 explicit Student(const string& s) :name(s), scores() {} //构造函数,参数为string类对象,赋值给name,关闭隐式转换 explicit Student(int n):name("No name"),scores(n){} //构造函数,初始化分数的数量(不是值),关闭隐式转换 Student(const string&s ,int n):name(s),scores(n){} //参数为name赋值,并声明有几个分数 Student(const string&s, const arr&a) :name(s), scores(a) {} //参数为name赋值,并将数组赋值给分数(调用的valarray的复制构造函数) Student(const string&s, const double*pd, int n) :name(s), scores(pd, n) {} //类似上面,分数有n个成员,并用pd数组初始化能初始化的那几个 ~Student() {} //析构函数,不声明也可以 double Average(); //返回平均分 const string& Name()const; //返回姓名 double & operator[](int i); //返回第n个分数,可被修改 double operator[](int i)const; //这个和上面有啥区别?不能被修改? //friend友元函数 friend istream& operator >> (istream&is, Student& Stu); //输入,一个单词 friend istream& getline(istream&is, Student& stu); //输入,但是一行 friend ostream& operator<<(ostream&os, const Student&stu); //输出 };
再补充类方法:
ostream& Student::scores_out(ostream& os)const { int i = 0; if (scores.size() > 0) { for (i = 0;i < scores.size();i++) { os << scores[i] << " "; if (i % 5 == 4) //5个数字换一行 os << endl; } if (i % 5 != 0) //如果最后一个数字不是5的倍数,那么换行(5的倍数的话在上面换行过了) os << endl; } else os << "empty" << endl; } double Student::Average() //返回平均分 { if (scores.size() > 0) { return scores.sum() / scores.size(); } else return 0; } const string& Student::Name()const { return name; } double & Student::operator[](int i) //返回分数 { return scores[i]; //使用了valarray的类方法 } double Student::operator[](int i)const { return scores[i]; } istream& operator>>(istream& is, Student&stu)//输入,一个单词 { is >> stu.name; return is; } istream& getline(istream&is, Student& stu) //输入,但是一行 { getline(is, stu.name); return is; } ostream& operator<<(ostream&os, const Student&stu) //输出 { os << stu.name << "'s scores: " << endl; stu.scores_out(os); return os; }
一个简单的测试程序:
#include<iostream> #include"stu.h" int main() { using namespace std; string a1 = "张三"; Student m1(a1); cout << m1; string a2 = "李四"; int b2 = 3; Student m2(a2, b2); cout << m2; valarray<double>b3 = { 1,2,3,4 }; string a3 = "王五"; Student m3(a3, b3); cout << m3; string a4 = "赵六"; double b4[4] = { 11.1,55.5,88.8,99.9 }; Student m4(a4, b4, 4); cout << m4; system("pause"); return 0; };
显示:
张三's scores: empty 李四's scores: 0 0 0 王五's scores: 1 2 3 4 赵六's scores: 11.1 55.5 88.8 99.9 请按任意键继续. . .