本章内容包括:
- has-a关系
- 包含对象成员的类
- 模板类和valarray
- 私有和保护继承
- 多重继承
- 虚基类
- 创建类模板
- 使用类模板
- 模板具体化
14.1 包含对象成员的类
14.1.1 valarray类简介(数值数组模板类)
valarray<int> q_values; //int数组
valarray<double> weights; //double数组
回顾:
vector<double> vd(n);
array<int, 5> ai;
使用构造函数的例子:
double gpa[5] = { 3.1, 3.5, 3.8, 2.9, 3.3 };
valarray<double> v1; //长度为0的空数组
valarray<int> v2(8); //指定长度的空数组
valarray<int> v3(10, 8); //所有元素(8)被初始化为指定值(10)的数组
valarray<double> v4(gpa, 4); //用常规数组(gpa)的值(前4个)进行初始化
valarray<int> v5 = { 20, 32, 17, 9 }; //初始化列表
valarray类方法:
- operator[]()
- size()
- sum()
- max()
- min()
14.1.2 Student类设计
is-a模型:水果->香蕉
has-a模型:午餐->香蕉
student类的成员函数可以通过name和score使用string类和valarray类的公有方法,来操作string类的对象name和valarray类的对象score,但并不是基类和派生的继承关系。
一个类对象通过另一个类对象调用另一个类的公有方法
14.1.3 Student类示例
//程序清单 14.1 studentc.h
#pragma once
#ifndef STUDENTC_H_
#define STUDENTC_H_
#include <iostream>
#include <string>
#include <valarray>
using namespace std;
class Student
{
private:
//typedef valarray<double> ArrayDb;
using ArrayDb = valarray<double>; //放私有意味着只可以在student类中使用ArrDb
string name;
ArrayDb scores;
ostream& arr_out(ostream& os) const;
public:
Student() : name("Null Student"), scores() {}
// 长度为0的double空数组
//转换函数
explicit Student(const string& s) : name(s), scores() {}
//string类型显式转换为student类类型,禁止隐式转换
explicit Student(int n) : name("Nully"), scores(n) {}
//int类型显式转换为student类类型,包含长度为n的double空数组
Student(const string& s, int n) : name(s), scores(n) {}
//string和int类型的转换为student类类型
Student(const string& s, const ArrayDb& a) : name(s), scores(a) {}
Student(const string& s, const double* pd, int n) : name(s), scores(pd, n) {}
~Student(){}
double Average() const;
const string& Name() const { return name; } //显示名称
double& operator[](int i) { return scores[i]; } //修改某一门成绩
double operator[](int i) const { return scores[i]; } //只读赋值
friend istream& operator>>(istream& is, Student& stu);
friend istream& getline(istream& is, Student& stu);
friend ostream& operator<<(ostream& os, const Student& stu);
};
#endif // !STUDENTC_H_
对于继承的对象,构造函数在成员初始化列表中使用类名来调用特定的基类构造函数:
hasDMA::hasDMA(const hasDMA & hs) : baseDMA(hs) {...}
对于成员对象,构造函数则使用成员名:
Student(const char * str, const double * pd, int n) : name(str), score(pd, n) {}
//程序清单 14.2
#include "studentc.h"
double Student::Average() const
{
if (scores.size() > 0) //元素个数
return scores.sum() / scores.size();
else
return 0;
}
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 << "Scores for " << stu.name << ":\n";
stu.arr_out(os);
return os;
}
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) //每行5个
os << endl;
}
if (i % 5 != 0)
os << endl; //显示光标换行
}
else
os << "Empty array!";
return os;
}
//程序清单 14.3
#include <iostream>
#include "studentc.h"
const int pupils = 3; //3个学士
const int quizzes = 5; //5门成绩
void set(Student& sa, int n);
int main(void)
{
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() << endl;
cout << "\nResult:\n";
for ( i = 0; i < pupils; i++)
{
cout << ada[i];
cout << "average: " << ada[i].Average() << endl << endl;
}
return 0;
}
void set(Student& sa, int n)
{
cout << "Enter the student's name: ";
//调用friend istream& getline(istream& is, Student& stu)
getline(cin, sa);
cout << "Enter " << n << " quiz scores:\n";
for (int i = 0; i < n; i++)
//调用double& operator[](int n);
cin >> sa[i];
while (cin.get() != '\n')
continue;
}
14.2 私有继承
- 包含将对象作为一个命名的成员对象添加到类中
- 私有继承将对象作为一个未被命名的继承对象添加到类中
14.2.1 Student类新版本
- 包含版本提供两个被显式命名的对象成员
- 私有继承提供两个无名称的隐式对象成员
//程序清单 14.4
#pragma once
#ifndef STUDENTI_H_
#define STUDENTI_H_
#include <iostream>
#include <string>
#include <valarray>
using namespace std;
class Student : private string, private valarray<double>
//私有继承使用关键字private,多个基类的继承称为多重继承
{
private:
typedef valarray<double> ArrayDb;
//using ArrayDb = valarray<double>;
ostream& arr_out(ostream& os) const;
public:
//基类类名代替成员名
Student() : string("Null Student"), ArrayDb() {}
//转换函数
explicit Student(const string& s) : string(s), ArrayDb() {}
//string类型显式转换为student类类型,禁止隐式转换
explicit Student(int n) : string("Nully"), ArrayDb(n) {}
//int类型显式转换为student类类型,包含长度为n的double空数组
Student(const string& s, int n) : string(s), ArrayDb(n) {}
//string和int类型的转换为student类类型
Student(const string& s, const ArrayDb& a) : string(s), ArrayDb(a) {}
Student(const string& s, const double* pd, int n) : string(s), ArrayDb(pd, n) {}
~Student() {}
double Average() const;
const string& Name() const { return (const string&) *this; }
//显示名称
double& operator[](int i) { return ArrayDb::operator[](i); }
//修改某一门成绩
double operator[](int i) const { return ArrayDb::operator[](i); }
//只读赋值
friend istream& operator>>(istream& is, Student& stu);
friend istream& getline(istream& is, Student& stu);
friend ostream& operator<<(ostream& os, const Student& stu);
using valarray<double>::max;
};
#endif // !STUDENTI_H_
1.类名代替成员名
//包含
Student(const string& s, const double* pd, int n) : name(s), scores(pd, n) {}
//私有
Student(const string& s, const double* pd, int n) : string(s), ArrayDb(pd, n) {}
2.访问基类方法:类名+::
//包含
double Student::Average() const
{
if (scores.size() > 0) //元素个数
return scores.sum() / scores.size();
else
return 0;
}
//私有
double Student::Average() const
{
if (ArrDb::size() > 0) //元素个数
return ArrDb::sum() / scores.size();
else
return 0;
}
3.访问基类对象:强制类型转换