(一四六)包含对象成员的类——第十四章

简介:

一个类对象,可以是另一个类的成员。就像string类可以作为其他类的类成员一样。

 

 

valarray类:

valarray类是一个模板类,模板类可以处理不同的数据类型。

其头文件是:<valarray>

使用和声明方式类似vector类和array类。

 

构造函数有以下几种:

①valarray<int>abc; //声明一个int类型的valarray类对象(是一个int数组),其长度为0

 

②valarray<double>abc(5); //声明一个长度为5double数组(即有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成员作为其初始化的参数,那么nameStudent类的顺序就必须位于该类成员之前。

 

 

因此有类定义:

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
请按任意键继续. . .


目录
相关文章
|
7月前
类的内部成员之五:内部类
类的内部成员之五:内部类
16 1
|
8月前
|
Java 程序员 PHP
C++的对象与类的含义
C++是一门面向对象的编程语言,理解C++需要掌握类(class)和对象(object)这两个概念。 C++ 中的类(Class)可以看做C语言中结构体(Struct)的升级版。结构体是一种构造类型,可以包含若干成员变量,每个成员变量的类型可以不同;可以通过结构体来定义结构体变量,每个变量拥有相同的性质。例如: #include <stdio.h> //定义结构体 Student struct Student{ //结构体包含的成员变量 char *name; int age; float score; }; //显示结构体的成员变量 void displ
38 0
|
8月前
|
算法 C++ 容器
关系类算法函数
关系类算法函数
|
10月前
|
编译器 C语言 C++
C++ 之什么是类 & 对象的关系?
C++ 之什么是类 & 对象的关系?
|
12月前
|
C++
【C++之多层继承】成员在各类的范围内的访问属性
【C++之多层继承】成员在各类的范围内的访问属性
|
Java
Java面向对象(2)--类的成员属性
Java面向对象(2)--类的成员属性
62 0
Java面向对象(2)--类的成员属性
|
设计模式 存储 前端开发
层次结构及对象的定义|学习笔记
快速学习层次结构及对象的定义
100 0
|
设计模式
单子设计模式 (对创建初始对象为静态,构造函数私有,返回值为对象的创建函数,private应用)
单子设计模式 (对创建初始对象为静态,构造函数私有,返回值为对象的创建函数,private应用)
单子设计模式 (对创建初始对象为静态,构造函数私有,返回值为对象的创建函数,private应用)
|
编译器 C++
C++中如何避免覆盖由继承而来的成员
C++中如何避免覆盖由继承而来的成员
128 0
|
Java
使用java反射机制读取任意类内部细节
使用java反射机制读取任意类内部细节
116 0