目录
1.包含对象成员的类
//class.h
#pragma once
#ifndef _CLASS_H_
#define _CLASS_H_
#include<iostream>
#include<string> //使用string类
#include<valarray> //使用valarray类
using namespace std;
class Student
{
private:
typedef valarray<double> ArrayDb; //typedef使得 ArrayDb为 valarray<double>类型的别名
string name;
ArrayDb scores;
ostream& arr_out(ostream& os)const;
public:
Student() :name("Null Student"), scores() { };
explicit Student(const string& s) :name(s), scores() { };
explicit Student(int n) :name("Nully"), scores(n) { };
Student(const string& s, int n) :name(s), scores(n) { };
Student(const string& s, const ArrayDb& a) :name(s), scores(a) { };
Student(const char* str, const double& pd, int n) :name(str), scores(pd, n) { };
~Student() { };
double Average()const;
const string& Name()const;
double& operator[](int i);
double operator[](int i)const;
//friend
friend istream& operator>>(istream& is, Student& std);
friend istream& getline(istream& is, Student& std); // 重载getline(cin,stringname)函数
friend ostream& operator<<(ostream& os, const Student& std);
};
#endif
#pragma once
//fuc.cpp
#include"class1.h"
double Student::Average()const
{
//调用了valarray类对象的size()和sum()函数
if (scores.size() > 0)
{
return scores.sum();
}
else
{
return 0;
}
}
const string& Student::Name()const
{
return name;
}
double& Student::operator[](int i)
{
//调用了valarrayy类对象的operator[]()函数 valarray<double>::operator[]()
return scores[i];
}
double Student::operator[](int i)const
{
return scores[i];
}
ostream& Student::arr_out(ostream& os)const
{
int i;
int lim = scores.size();
if (lim > 0)
{
for (int i = 0; i < lim; i++)
{
os << scores[i] << " ";
if (i % 5 == 4)
{
os << endl;
}
}
if (i % 5 != 0)
{
os << endl;
}
}
else
{
os << " empty array";
}
return os;
}
istream& operator>>(istream& is, Student& stu)
{
is >> stu.name;
return is;
}
istream& getline(istream& is, Student& stu)
{
getline(is, stu.name); //getline(cin,stringname)
return is;
}
ostream& operator<<(ostream& os, const Student& stu)
{
os << "Scores for " << stu.name << ": \n";
stu.arr_out(os);
return os;
}
//main.cpp
#include"class1.h"
void set(Student& sa, int n);
const int pupils = 3;
const int quizzes = 5;
int main()
{
//创建一个含有三个元素的数组,其中每个元素保存五个考试成绩(即调用scores(quizzes)构造函数创建了valarray<double>对象
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 << "\n Result: \n";
for (i = 0; i < pupils; i++)
{
cout << endl << ada[i];
cout << "average " << ada[i].Average() << endl;
}
cout << "Done" << endl;
system("pause");
return 0;
}
void set(Student& sa, int n)
{
cout << "Please enter the student's name: ";
getline(cin, sa); //重载的getline函数,可以用与给类对象输入
cout << "Please enter " << n << " quiz scores: \n";
for (int i = 0; i < n; i++)
{
//类中定义了成员函数operator[]().所以Student对象使用sa[i]时,即为调用sa中的scores[i]
cin >> sa[i]; //sa[i]即为valarray<double>类scores对象的数组,cin>>sa[i]即为此数组赋值
}
while (cin.get() != '\n')
{
continue;
}
}
知识点:
1.对于单参数的构造函数,最好加上explicit以防止其作为转换函数进行隐式转换。
eg:
explicit Student(const string& s) :name(s), scores() { };
explicit Student(int n) :name("Nully"), scores(n) { };
2.valarray类 -- 头文件#include<valarray>
1)用处:储存数据对象,并可以使用该类中含有的operaot[](),size(),sum(),max(),min()等函数对舒徐对象进行相应的处理。
2)相关构造函数:
eg:
Student(const string& s, const ArrayDb& a) :name(s), scores(a) { };
//scores(a)即创建一个scores[a]的数组,其中共有a个元素
Student(const char* str, const double& pd, int n) :name(str), scores(pd, n) { };
//scores(pd,n)也是创建一个scores[a]的数组,同时为其中的a个元素均赋值为pd
3)语法: valarray<typename> name
eg:
typedef valarray<double> ArrayDb; //typedef使得 ArrayDb为 valarray<double>类型的别名
ArrayDb scores;
//等同于:
valarray<double> scores;
3.成员初始化的顺序:当初始化列表包含多个项目时,这些项目被初始化的顺序为它们被声明的顺序,而不是它们在初始化列表中的顺序。
eg:
typedef valarray<double> ArrayDb;
ArrayDb scores;
string name;
Student(const string& s, int n) :name(s), scores(n) { };
//由于scores先声明,name后声明,即使在成员初始化的列表中是name在前,初始化过程中也是先初始化先声明的scores再初始化后声明的name。
2.私有继承
//class.h
#pragma once
#ifndef _CLASS_H_
#define _CLASS_H_
#include<iostream>
#include<valarray> // valarray<typename> name
#include<string>
using namespace std;
class Student :private string, private valarray<double> //私有继承,继承string类和valarray<double>类
{
private:
typedef valarray<double> ArrayDb;
ostream& arr_out(ostream& os)const;
public:
//因为私有继承不显式的创建子对象,所以构造函数创建基类对象时在成员初始化部分只能直接调用基类的构造函数
Student() :string("Null Student"), ArrayDb() { };
explicit Student(const string& s) :string(s), ArrayDb() { };
explicit Student(int n) :string("Nully"), ArrayDb(n) { };
Student(const string& s, int n) :string(s), ArrayDb(n) { };
Student(const string& s, const ArrayDb& a) :string(s), ArrayDb(a) { };
Student(const char* str, const double& pd, int n) :string(str), ArrayDb(pd, n) { };
~Student() { };
double Average()const;
const string& Name()const;
double& operator[](int i);
double operator[](int i)const;
//friend
friend istream& operator>>(istream& is, Student& std);
friend istream& getline(istream& is, Student& std); // 重载getline(cin,stringname)函数
friend ostream& operator<<(ostream& os, const Student& std);
};
#endif
//fuc.cpp
//私有继承通过使用类名和作用域解析运算符来调用基类的方法(没有显式的子对象成员,无法通过子对象成员来访问基类的方法)
//通过强制类型转换访问基类的对象,通过强制类型转换访问基类的友元函数
#include"class.h"
double Student::Average()const
{
//调用了valarray类对象的size()和sum()函数
if (ArrayDb::size() > 0)
{
return ArrayDb::sum()/ArrayDb::size();
}
else
{
return 0;
}
}
const string& Student::Name()const
{
return (const string&)*this;
}
double& Student::operator[](int i)
{
//调用了valarrayy类对象的operator[]()函数 valarray<double>::operator[]()
return ArrayDb::operator[](i); //访问基类ArrayDb中的operator[]函数,并传入参数i
}
double Student::operator[](int i)const
{
return ArrayDb::operator[](i);
}
ostream& Student::arr_out(ostream& os)const
{
int i;
int lim = ArrayDb::size();
if (lim > 0)
{
for (i = 0; i < lim; i++)
{
os << ArrayDb::operator[](i) << " ";
if (i % 5 == 4)
{
os << endl;
}
}
if (i % 5 != 0)
{
os << endl;
}
}
else
{
os << " empty array";
}
return os;
}
istream& operator>>(istream& is, Student& stu)
{
is >> (string &)stu; //为string类对象输入,而又没有显式的子成员对象,只能使用强制类型转换。
return is;
}
istream& getline(istream& is, Student& stu)
{
getline(is, (string&)stu); //getline(cin,stringname)
return is;
}
ostream& operator<<(ostream& os, const Student& stu)
{
os << "Scores for " << (string&)stu << ": \n";
stu.arr_out(os);
return os;
}
//main.cpp
#include"class.h"
void set(Student& sa, int n);
const int pupils = 3;
const int quizzes = 5;
int main()
{
//创建一个含有三个元素的数组,其中每个元素保存五个考试成绩(即调用scores(quizzes)构造函数创建了valarray<double>对象
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 << "\n Result: \n";
for (i = 0; i < pupils; i++)
{
cout << endl << ada[i];
cout << "average " << ada[i].Average() << endl;
}
cout << "Done" << endl;
system("pause");
return 0;
}
void set(Student& sa, int n)
{
cout << "Please enter the student's name: ";
getline(cin, sa); //重载的getline函数,可以用与给类对象输入
cout << "Please enter " << n << " quiz scores: \n";
for (int i = 0; i < n; i++)
{
//类中定义了成员函数operator[]().所以Student对象使用sa[i]时,即为调用sa中的scores[i]
cin >> sa[i]; //sa[i]即为valarray<double>类scores对象的数组,cin>>sa[i]即为此数组赋值
}
while (cin.get() != '\n')
{
continue;
}
}
知识点:
1.私有继承private:基类的公有成员和保护成员都将成为派生类的私有成员(只可通过派生类的成员函数来访问)。
2.私有继承提供无名称的子对象成员,并通过构造函数的成员初始化调用相应的构造函数来进行初始化。(因此只能一个类只能创建一个子对象成员,否则会造成二义性)
3.私有继承通过使用类名和作用域解析运算符来调用基类的方法(没有显式的子对象成员,无法通过子对象成员来访问基类的方法)
4.访问基类对象:通过强制类型转换,将派生类对象强制转换成基类对象并进行相应的访问。
eg:
const string& Student::Name()const
{
return (const string&)*this;
}
//解析:this是指向调用对象的指针,*this则是调用对象本身,而通过(const string&)的强制转换,可以将调用对象*this转换成string类对象。
且强制类型转换需要配套: Student -> string , Student & -> string &...
5.访问基类的友元函数:类似与public继承中派生类访问基类友元函数的方法,通过强制类型转换改变参数的类型以匹配基类的友元函数。
eg:
ostream& operator<<(ostream& os, const Student& stu)
{
os << "Scores for " << (string&)stu << ": \n";
stu.arr_out(os);
return os;
}
//调用的是基类的友元函数,通过强制类型转换参数类型以匹配基类的友元函数来完成
3.各类继承方式:
1.在使用保护继承(protected)和私有继承(private)时,若想让基类的方法在派生类的外面可用,可以通过包装,在派生类的方法中包含基类的方法:
eg:
double Student::sum() const
{
return valarray<double>::sum();
}
//保护继承和私有继承没有显式的子成员对象,只能通过类名+成员解析运算符来访问基类的方法
2. 通过using声明(类似于名称空间),来指出派生类可以使用的特定基类成员(在公有部分声明)。
eg:
class Student :private string , private valarray<double>
{
public:
using valarray<double>::max;
using valarray<double>::min;
}
int main()
{
Student s1;
s1.max();
s2.min();
}
1):使用using声明某基类函数后,派生类对象就可以像访问自己的成员函数一样访问该函数。
2):使用using声明函数并不是调用函数,所以在 classname:: 后只需要加上函数名,无需加上括号。