valarray class containing object members (cpp Chapter 14)

C++ code reuse

1. Public inheritance can be achieved

2. Containment, private inheritance, and protected inheritance are used to implement the has-a relationship, that is, the new class will contain objects of another class.

(Using class members that are themselves objects of another class is called containing (composition or hierarchy).)

3. Function templates, class templates

Valarray class constructor example

double gap[5] = {3.1,3.5,3.8,2.9,3.3};
valarray<double> v1; //Create an empty array of double type
valarray<int> v2(8); //Create 8 int type arrays
valarray<int> v3(10,8); //Create 8 int type arrays, each number in the array is 10
valarray<double> v4(gap,4);//Take out the first four elements of the gap array to fill the v4 array
valarray<int> v5 = { 20,32,17,9 };//C++11

Class method example:

operator[](): access each element
size(): Returns the number of elements contained
sum() : Returns the sum of all elements
max(): Returns the largest element
min(): Returns the smallest element

Example: Enter the test scores of each student (has_a relationship, students have names and a set of test scores)

Valarray class contains string class and valarray class

Use a string object to represent the student’s name, and valarray to represent the test score.

Declaring it private means that the member functions of the Valarray class can access and modify the name and scores objects using the public interfaces of the string and valarray classes. But this cannot be done outside the class. Name and scores can only be accessed through the public interface of the Valarray class. Usually described as: The Valarray class obtains the implementation of its member objects, but does not inherit the interface.

1. Code: (using the include method)

valarray.h

#ifndef VALARRAY_H_
#define VALARRAY_H_
#include <iostream>
#include <string>
#include <valarray>
using namespace std;

//Chapter 14 14.1 valarray class containing member objects
class Student
{
private:
typedef valarray<double> ArrayDb;
string name;
ArrayDb scores;//valarray<double> ArrayDb
public:
Student():name("Null student"),scores(){} //Member initialization list
explicit Student(const string & amp;s):name(s), scores() {} //explicit turns off implicit conversion so that it can only be called explicitly
explicit Student(int n) :name("Nully"), scores(n) {}
Student(const string & amp;s,int n) :name(s), scores(n) {}
Student(const string & amp;s, const ArrayDb & amp;a) :name(s), scores(a) {}
Student(const string & amp;s, const double *pd,int n) :name(s), scores(pd,n) {}
~Student(){}

double Average() const; //The average score cannot be modified
const string &Name() const;
double & amp;operator[](int n); //stu[0]=100;
double operator[](int n) const;//a=stu[0]

friend istream & amp;operator >>(istream & amp;is, Student & amp;stu);//Friend function overloads input and output operators
friend istream & amp;getline(istream & amp;is, Student & amp;stu);
friend ostream & amp;operator<<(ostream & amp;os, Student & amp;stu);
};


#endif // !VALARRAY_H_

valarray.cpp

#include "valarray.h"

double Student::Average() const
{
if (scores. size() > 0)
return scores.sum() / scores.size();
else
return 0.0;
}

const string & Student::Name() const
{
return name;
}

double & Student::operator[](int n)
{
return scores[n];
}

double Student::operator[](int n) const
{
return scores[n];
}

istream & amp; operator>>(istream & amp; is, Student & amp; stu)
{
is >> stu.name;
return is;
}

istream & amp; getline(istream & amp; is, Student & amp; stu)
{
getline(is, stu.name);
return is;
}

ostream & amp; operator<<(ostream & amp; os, Student & amp; stu)
{
os << "Scores for" << stu.name << ":" << endl;//Display the student's name and scores in each subject
int i;
int lim = stu.scores.size();
if (lim > 0)
{
for ( i = 0; i < lim; i + + )
{
os << stu.scores[i] << " ";
if (i % 5 == 4)
os << endl;
}
if (i % 5 != 0)
os << endl;
}
else
os << "Empty array" << endl;
return os;
\t  
}


main.cpp

#include <iostream>
#include "valarray.h"

using namespace std;
const int pupils = 3; //Number of people
const int quizzes = 5;//Everyone has 5 scores
void set(Student &sa, int n);
int main()
{
Student ada[pupils] = { Student(quizzes),Student(quizzes) ,Student(quizzes) };
int i;
for (i = 0; i <pupils; i + + )
set(ada[i], quizzes);
\t
cout << "\
 Student List:" << endl;
for (i = 0; i <pupils; i + + )
cout << ada[i].Name() << endl;//Display the name of each student

cout << "\
 Result List:" << endl;
for (i = 0; i <pupils; i + + )
cout << ada[i];
cout << "Average:" << ada[i].Average() << endl;
return 0;
}

void set(Student &sa, int n)
{
cout << "Please enter the student's name:";
getline(cin, sa);
cout << "Please enter:" << n << "quiz scores:" << endl;
for (int i = 0; i < n; i + + )
cin >> sa[i];
while (cin.get() != '\
');
}



operation result:

2. Using private inheritance methods

With private inheritance, the public methods of the base class become private methods of the derived class.

Thedifference between inclusion and private inheritance:

1. Containment provides two explicitly named object members (name and scores), while private inheritance provides two unnamed sub-object members.

2. The constructor of private inheritance uses initialization member list syntax, which uses the class name instead of the member name to identify the constructor.

Contains: Use member name name and scores

Student(const string & amp;s, const double *pd,int n) :name(s), scores(pd,n) {}

Private inheritance: using string and ArrayDb

Student(const string & amp;s, const double *pd,int n) :string(s), ArrayDb(pd,n) {}

Modify the containing code to make it privately inherited:
1. Access base class methods

Contains: Use valarray’s methods, such as size() and sum(), to call methods using the object name

double Student::Average() const
{
if (scores. size() > 0)
return scores.sum() / scores.size();
else
return 0.0;
}

Private inheritance (only methods of the base class can be used in methods of the derived class): Use the class name and scope and scope operators to call methods of the base class

double Student::Average() const
{
if (ArrayDb::size() > 0) //Method to access base class using class name + scope resolution operator
return ArrayDb::sum() / ArrayDb::size();
else
return 0.0;
}

2. Access base class objects

Includes:name is a string class object

const string & amp; Student::Name() const
{
return name;
}

Private inheritance: The string object has no name. How does the code of the Student class access the string object? Use cast

const string & amp; Student::Name() const
{
return (const string & amp;)*this; //Access base class object through forced type conversion
}

*this is the object on which the method is called. Returns a reference to the inherited string object in the Student object used for this method.

3. Access the friend function of the base class

The class name display qualified function name does not apply to friend functions, because friend functions are not member functions and do not belong to the class.

Contains:

ostream & amp; operator<<(ostream & amp; os, Student & amp; stu)
{
os << "Scores for" << stu.name << ":" << endl;//Display the student's name and scores in each subject
....
}

Private inheritance: Use explicit conversions to call the correct functions for the base class

ostream & amp; operator<<(ostream & amp; os, const Student & amp; stu)
{
os << "Scores for" << (string & amp;)stu << ":" << endl;//Display the student's name and scores in each subject
.....
}

Overall code:

valarray.h

#ifndef VALARRAY_H_
#define VALARRAY_H_
#include <iostream>
#include <string>
#include <valarray>
using namespace std;

//Chapter 14 14.2 Private inheritance
class Student:private string,private valarray<double> //Private inheritance
{
private:
typedef valarray<double> ArrayDb;
// string name;
// ArrayDb scores;//valarray<double> ArrayDb
public:
Student():string("Null student"), ArrayDb(){} //Member initialization list
explicit Student(const string & amp;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 & amp;s, const ArrayDb & amp;a) :string(s), ArrayDb(a) {}
Student(const string &s, const double *pd,int n) :string(s), ArrayDb(pd,n) {}
~Student(){}

double Average() const; //The average score cannot be modified
const string &Name() const;
double & amp;operator[](int n); //stu[0]=100;
double operator[](int n) const;//a=stu[0]

friend istream & amp;operator >>(istream & amp;is, Student & amp;stu);
friend istream & amp;getline(istream & amp;is, Student & amp;stu);
friend ostream & amp;operator<<(ostream & amp;os,const Student & amp;stu);
};


#endif // !VALARRAY_H_

valarray.cpp

#include "valarray.h"

double Student::Average() const
{
if (ArrayDb::size() > 0) //Method to access base class using class name + scope resolution operator
return ArrayDb::sum() / ArrayDb::size();
else
return 0.0;
}

const string & Student::Name() const
{
return (const string & amp;)*this; //Access base class object through forced type conversion
}

double & Student::operator[](int n)
{
return ArrayDb::operator[](n);
}

double Student::operator[](int n) const
{
return ArrayDb::operator[](n); //Method to access the base class using class name + scope resolution operator
}

istream & amp; operator>>(istream & amp; is, Student & amp; stu)
{
is >> (string &)stu;
return is;
}

istream & amp; getline(istream & amp; is, Student & amp; stu)
{
getline(is, (string & amp;)stu);
return is;
}

ostream & amp; operator<<(ostream & amp; os, const Student & amp; stu)
{
os << "Scores for" << (string & amp;)stu << ":" << endl;//Display the student's name and scores in each subject
int i;
int lim = stu.ArrayDb::size();
if (lim > 0)
{
for ( i = 0; i < lim; i + + )
{
os << stu.ArrayDb::operator[](i) << " ";
if (i % 5 == 4)
os << endl;
}
if (i % 5 != 0)
os << endl;
}
else
os << "Empty array" << endl;
return os;
\t  
}


main.cpp

#include <iostream>
#include "valarray.h"

using namespace std;
const int pupils = 3; //Number of people
const int quizzes = 5;//Everyone has 5 scores
void set(Student &sa, int n);
int main()
{
Student ada[pupils] = { Student(quizzes),Student(quizzes) ,Student(quizzes) };
int i;
for (i = 0; i <pupils; i + + )
set(ada[i], quizzes);
\t
cout << "\
 Student List:" << endl;
for (i = 0; i <pupils; i + + )
cout << ada[i].Name() << endl;//Display the name of each student

cout << "\
 Result List:" << endl;
for (i = 0; i <pupils; i + + )
cout << ada[i];
cout << "Average:" << ada[i].Average() << endl;
return 0;
}

void set(Student &sa, int n)
{
cout << "Please enter the student's name:";
getline(cin, sa);
cout << "Please enter:" << n << "quiz scores:" << endl;
for (int i = 0; i < n; i + + )
cin >> sa[i];
while (cin.get() != '\
');
}



result:

3. Scenarios for using private inheritance:

1. Assuming that the class contains protected members (which can be data members or member functions), such members are available in the derived class, but not available outside the inheritance hierarchy. By using composition to include such a class within another class, the latter will not be a derived class but will be outside the inheritance hierarchy and therefore will not have access to protected members. But what you get through private inheritance will be the derived class, so you can access the protected members.

2. When virtual functions need to be redefined. Derived classes can redefine virtual functions, but inclusion cannot. Using private inheritance, the redefined functions will only be used in the class, not public.

Generally: inclusion should be used to establish a has-a relationship; if the new class needs to access protected members of the original class, or needs to redefine a virtual function, use private inheritance.