有时候在类中使用别的类的组件会方便很多。这就是has-a关系。比如学生类中有姓名和一系列考试成绩,姓名用string类对象表示,一系列成绩用valarray类对象表示。这就不是is-a关系了,这是student类中has string姓名和valarray成绩 关系。
valarray类简单介绍:
使用方法
- 声明方式:
valarray<int> q_value
尖括号内说明这是个int类型模板类- 使用对象:(构造函数)
double arr[5] = {3.1,3.5,3.8,3.9,4.0}; valarray<double> v1 一个size为0的double array valarray<int> v2(8) 一个size为8的int array valarray<int> v3(10,8) 一个size为8且每个值初始化为10的int array valarray<double> v4(arr,4) 一个size为4,初始值为arr数组前四个 valarray<int> v5 = {20,32,1,2} 初始化列表
- 使用对象(基本方法)
operator[]():访问各个元素
size() : 返回包含的元素数
sum() : 返回和
max() , min()
Student类对象设计
- 关系has-a的类,类可以获得实现但不能继承接口。而is-a的类,获得接口是可以的。
直观来说:is-a关系就是派生类可以使用基类的方法(virtual or not)(纯虚函数只提供接口不提供实现,就是派生类需要额外定义才能实现。接口就是抽象,比如上面的size()就是接口,而size()的功能被实现就是实现。)- 比如string+string有意义,但student+student对象是没意义的。但有些借口对新类而言是有意义的,比如string的operator<()用到student类对象可以按姓名顺序排序,则可以定义Student::operator<(),然后在内部使用string::operator<()
类声明
#ifndef STUDENTC_H_
#define STUDENTC_H_
#include<iostream>
#include<valarray>
#include<string>
class Student
{
private:
typedef std::valarray<double> AR;
std::string name;
AR scores;
std::ostream& arr_out(std::ostream& os) const;
public:
Student() : name("No"),scores(){}
explicit Student(const std::string& s):name(s),scores() {}
explicit Student(int n): name("Jeff"),scores(n){}
Student(const std::string& s,int n):name(s),scores(n){}
Student(const char* str,const double* pd, int n)
:name(str),scores(pd,n){}
~Student(){}
double average() const;
const std::string& Name() const;
double& operator[](int i);
double operator[](int i) const;
friend std::istream& operator>>(std::istream& is,Student& s);
friend std::istream& getline(std::istream& is,Student& s);
friend std::ostream& operator<<(std::ostream& os,const Student& s);
};
#endif
程序解析:
- explicit: 可以用一个参数调用的构造函数用作从参数类型到类类型的隐式转换函数,但这通常不是好主意,比如int到Student的转换是毫无意义的,所以用explicit避免隐式转换。
- 初始化被包含的对象:
用成员初始化列表的方式,如果省略初始化列表,则C++将使用成员对象(既string和valarray)的默认构造函数。而用成员初始化列表name(str)则会调用string(const char*)。- private那声明的函数也叫辅助函数,辅助函数可以用在类方法中。
源代码
#include"studentc.h"
using std::cout;
std::ostream& Student::arr_out(std::ostream& os) const
{
int i,lim=scores.size();
if(lim>0)
{
for(i=0;i<lim;i++)
{
os<<scores[i]<<" ";
if(i%5==4)
os<<std::endl;
}
if(i%5!=0)
os<<std::endl;
}
else
os<<"empty scores ";
return os;
}
double Student::average() const
{
if(scores.size()!=0)
return scores.sum()/scores.size();
else
return 0;
}
const std::string& Student::Name() const
{
return name;
}
double& Student::operator[](int i)
{
return scores[i];
}
double Student::operator[](int i) const
{
return scores[i];
}
std::istream& operator>>(std::istream& is,Student& s)
{
for(int i=0;i<s.scores.size();i++)
is>>s.scores[i];
return is;
}
std::istream& getline(std::istream& is,Student& s)
{
getline(is,s.name);
return is;
}
std::ostream& operator<<(std::ostream& os,const Student& s)
{
os<<"Scores for "<<s.name<<": ";
s.arr_out(os);
return os;
}
程序解析:
- 使用被包含对象的接口
比如scores.size(),就是使用了valarray对象scores的接口size,但不能继承接口,既不能Student.size()。ostream& operator<<(ostream& os,const Student& s)
首先s.name是一个string对象,则将调用函数
ostream& operator<<(ostream& os,const string& s)
,该函数位于string类型中- valarray也想有<<实现,但没有这种实现,所以可以定义一个私有辅助函数,然后在<<函数中使用辅助函数来实现功能。
主程序
#include"studentc.h"
using namespace std;
void set(Student& s,int n);
const int pupils =3;
const int quizzes = 5;
int main()
{
Student ada[pupils]={Student(quizzes),Student(quizzes),Student(quizzes)};
int i;
for(i=0;i<pupils;i++)
set(ada[i],quizzes);
cout<<"\nStudent list:\n";
for(i=0;i<pupils;i++)
{
cout<<ada[i].Name()<<" ";
}
cout<<"\nStudent scores:\n";
for(i=0;i<pupils;i++)
{
cout<<ada[i];
cout<<"Average: "<<ada[i].average()<<endl;
}
cout<<"Done";
return 0;
}
void set(Student& s,int n)
{
cout<<"Enter the name: ";
getline(cin,s);
cout<<"Enter "<<n<<" scores: ";
for(int i=0;i<n;i++)
cin>>s[i];
while(cin.get()!='\n')
continue;
}
程序解析:
- 使用set函数来设置类对象值,在set中调用成员函数getline和>>函数
- 使用类数组,数组大小表示学生个数。
cout<<ada[i].Name()<<" ";
将调用的是string类中的<<,不是Student。
cout<<ada[i]
就是调用Student类的<<
关于类对象的调用,应该比较清楚了