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

一个类对象,可以是另一个类的成员。就像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
请按任意键继续. . .
时间: 2024-07-29 13:29:02

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

(一二七)动态内存和类——第十二章

面对基本类型的时候,我们可以使用动态内存(new和delete).   而面对类的时候,也可以使用动态内存,只不过使用方法有区别.   例如声明一个私有成员,想要这个私有成员的值为一个字符串(但这个字符串是什么是未知的). 首先,不考虑用char word[40];这样的.原因有两点:①实际字符串可能超过40个字符:②对于没有超过的,很可能导致内存浪费(例如创建了1w个对象,有9000个只要一个字符长度,1000个需要40个字符长度):   因此,可以使用指针,让指针来指向字符串. 但单纯用指针

派生类与派生类对象对基类成员的访问

区分"派生类对象"和"派生类"对基类成员的访问权限.    "派生类对象"对基类成员的访问权限:      (1)对于公有继承,只有基类的公有成员可以被"派生类对象"访问,其他(保护和私有)成员不能被访问.      (2)对于私有继承和保护继承,基类中所有成员都不能被"派生类对象"访问.    "派生类"对基类中成员的访问权限:     (1)对于公有继承,基类中的公有成员和保护成

c++-如果类中有多个同一个类的多个对象成员,如何初始化。

问题描述 如果类中有多个同一个类的多个对象成员,如何初始化. 如果类中有多个同一个类的多个对象成员,如何初始化. 类名:构造函数名(形参):对象1(参数表),对象2(参数表)... 解决方案 就是像你写的那样初始化,也可以写在构造函数里. 解决方案二: 就是通过构造函数 或者放到一个数组中循环初始化 解决方案三: 1.普通的变量:一般不考虑啥效率的情况下 可以在构造函数中进行赋值.考虑一下效率的可以再构造函数的初始化列表中进行. 2.static 静态变量: static变量属于类所有,而不属于

wcf-WCF中数据契约类包含数组成员时客户端报错,怎么解决

问题描述 WCF中数据契约类包含数组成员时客户端报错,怎么解决 WCF中数据契约类包含数组成员时客户端报错,怎么办呢?这个声明为数据契约的类是当成返回值传给客户端的,当数组成员赋值为null的时候客户端能正确接收到数据,但是当给这个数组成员赋予一个数组时客户端就不能读取这个返回的数据了.有高手有类似经验吗?望不啬赐教啊,很急 解决方案 你的数组元素的类型是什么,你的详细报错信息是什么 参考https://social.microsoft.com/Forums/nl-NL/da8c4e56-0a5

COM组件对象与.NET类对象的相互转换

对象|转换 运行环境:Visual Studio.NET Beta2, VC7, C#参考资料:MSDN级别:入门级 一.前言 COM组件对象与.NET类对象是完全不同的,但为了使COM客户程序象调用COM组件一样调用.NET对象,使.NET程序象使用.NET对象一样使用COM组件,MS使用了wrapper技术.本文详细介绍了两种不同的wrapper技术,并给出了简单的代码实例. 二.COM wrapper简介 传统的COM对象与.NET框架对象模型有以下几点不同:(1).COM对象的客户必须自

【C/C++学院】0819-/类的成员函数与const-mutable /构造与析构/拷贝构造deletedefault以及深浅拷贝/静态成员函数成员变量类在内存的存储默认参数/友元类以及友元函数

类的成员函数与const-mutable  成员函数 Fushu.h #pragma once #include <iostream> class fushu { public: int x; int y; public: fushu(); ~fushu(); void show(); inline void showall(int x, int y);//显式内联 void setxy(int x, int y);//编译器优化,默认隐式内联 void show(int x, int y);

详解C++编程中类的声明和对象成员的引用_C 语言

C++类的声明和对象的创建 类是创建对象的模板,一个类可以创建多个对象,每个对象都是类类型的一个变量:创建对象的过程也叫类的实例化.每个对象都是类的一个具体实例(Instance),拥有类的成员变量和成员函数. 与结构体一样,类只是一种复杂数据类型的声明,不占用内存空间.而对象是类这种数据类型的一个变量,占用内存空间. 类的声明 类是用户自定义的类型,如果程序中要用到类,必须先进行声明,或者使用已存在的类(别人写好的类.标准库中的类等),C++语法本身并不提供现成的类的名称.结构和内容. 一个简

[Java] 方法锁、对象锁和类锁的意义和区别

版权声明:请尊重个人劳动成果,转载注明出处,谢谢! 目录(?)[+] 首先的明白Java中锁的机制 synchronized  在修饰代码块的时候需要一个reference对象作为锁的对象.  在修饰方法的时候默认是当前对象作为锁的对象.  在修饰类时候默认是当前类的Class对象作为锁的对象.   线程同步的方法:sychronized.lock.reentrantLock分析 方法锁(synchronized修饰方法时) 通过在方法声明中加入 synchronized关键字来声明 synch

jQuery对象中的类数组操作

我们都知道jQUery对象中有一个类数组的元素包装集,该集合类似js中的数组一样拥有length属性,因此我们称此为类数组,下面我们就来总结下这个jQuery对象中的类数组时如何进行操作的,看看我们的jQuery为我们都提供了哪些可用的方法: size():很明显,它应该是返回包装集中的元素个数,如$('a').size()表示链接元素的个数: get(index):当没指定index时就默认取包装集中所有元素,并以js中的数组形式返回,如果指定了index,则返回下标为index对应的元素,如