第十六天(C++代码重用)

本文探讨了C++中代码复用的方法,包括使用成员对象、封装及继承等,并介绍了Valarray类的使用方式及其在Student类中的应用实例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

      看下日期,一共3个多月没写。再次“执笔”亦需下不少的决心。今天的不长,一来表明自己不会太监。二来亦需重回写程序的状态。老规矩,还是会用英文写的。


2012-3-10(Reusing Code in C++ part I)

1. Big Picture

One of the main goals of C++ is to facilitate the reuse of code. Public inheritance is one method to achieve this goal, but not the only one. We discuss other means this chapter. 

       One of these means is to use class members that are themselves objects of another class. This is referred to as containment or composition or layering.Another option is to use private or protected inheritance. All of them(containment, private inheritance and protected inheritance) are typically used to implement has-a relationships—that is, relationships for which the new class has an object of anther class.

2.valarray Class

This class is targeted to deal with numeric values or with classes with similar properties. It have to be defined as a template class, so that it can process different kind of data. To use it, you need to know its constructors. For example:

(copy from:Click here to link)

int init[]= {10,20,30,40};

valarray<int> first;              // (empty)
valarray<int> second (5);         // 0 0 0 0 
valarray<int> third (10,3);       // 10 10 1
valarray<int> fourth (init,4);    // 10 20 30 4
valarray<int> fifth (fourth);     // 10 20 30 40
Note that you can create an array initialized using the values from an ordinary array. Next, some useful methods:
operator []()//provides access to individual element
size()       //returns the number of element
sum()        //returns the sum of the element
… …        //etc
3. Using has-a relationship

The following task is to define a class named Student, whose private members are student's name and scores.They are both use the classes in STL(string and valarray). This Student class must have the following fundamental function:

  •        Create Student objects by different way; 
  •        I/O system.

I. Header File Design

#ifndef STUDENT_H_
#define STUDNET_H_

#include <iostream>
#include <string>
#include <valarray>

using std::string;
using std::valarray;
using std::ostream;
using std::istream;

class Student
{
private:
	typedef valarray<double> DbArray;
	string name;
	DbArray scores;		//two contained object
	void sco_out() const;
public:
	Student(): name("Null"),scores() {}                  //default constructor
	Student(const string& s): name(s),scores() {}        //create student without score
	explicit Student(int n): name("Null"),scores(n) {}   //create student without name
	Student(const string& s, int n): name(s),scores(n) {}//student have given name and empty score
	//student have given name and given scores
	Student(const string& s, DbArray d): name(s),scores(d) {}                //by valarray object
	Student(const string& s, const double* d, int n): name(s),scores(d,n) {} //by ordinary array
	~Student(){}
	/******** I/O system ********/
	void setName(const string& s) {name = s; }
	void setScores();
	const string& Name() const;	
	double average() const;
	double operator [](int i) const;
	double& operator [](int i);

	friend istream& operator >>(istream& is, Student& st);	//accept one word
	friend istream& getline(istream& is, Student& st);	//accept one line
	friend ostream& operator <<(ostream& os, Student& st);
};
#endif
i. Keyword typedef 

The statement

                    typedef valarray<double> DbArray;

means enables the remaining code to use the more convenient notation DbArray instead of valarray<double>. Placing this typedef in the private portion of the class definition means that it can be used internally in the Student implementation but not by outside users of the Student class.

ii. Keyword explicit

Recall that a constructor that can be called with one argument serves as an implicit conversion function from the argument type to the class type. For example, one of the constructors of string class is string(const string& str).The following statement:

                                                 string str = "Example";

is equal to

                            string str = string("Example");

Supposed a programmer wanted to type the following code:

                      Student stu("Tom", 5);//create a student object 

                   stu[2] = 85;          //modify the value of stu.scores[2]

But this inattentive programmer typed stu = 85 instead of stu[5]= 85. If the constructor omitted explicit, the second statement will call constructor named Student(int n). Then assignment would replace the original stu with the temporary object. With explicit in place, the compiler will catch the assignment operator as an error. So the keyword explicit turns off implicit conversion.

iii. Initializer List

We had discussed about initializer list syntax in the inheritance portion. In short, initializer list allows you to use existent constructors(designed by yourself or others) to initialize members of object. For example:

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

Here, name(s) invokes the string(const char*) constructor and scores(d,n) invokes the valarray<double>(const double*,int) constructor.

iv. Initialization Order

Initializer list will be initialized in a certain order. If you type the following code: 

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

The name member would still be initialized first because it is declared first in the class definition. It's not important for this example, but it would be important if the code used the value of one member as part of the initialization expression for a second member.

v. Private Function

Here, we declare a private function. Private functions can be only invoked by its “own” function(member function and friend). In this example, sco_out() is designed for printing scores in order.

II. Implementation Design

#include "Student.h"

using std::endl;
using std::cout;
using std::cin;

void Student::sco_out() const
{
	int size = scores.size();

	if(size == 0) cout << "Empty Score\n";
	else
	{
		for(int i = 0; i< size; i++)
		{
			cout << scores[i] << " ";
			if(i % 5 == 4) cout << endl;
		}
		if(size % 5 != 0) cout << endl;
	}

}

void Student::setScores()
{
	int size = scores.size();
	cout << "Input " << name << "'s scores: \n";
	for(int i = 0; i< size; i++)
	{		
		cout << "#" << i + 1 << ": "; 
		cin >> scores[i];
	}
}

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

double Student::average() const
{
	if (scores.size() == 0) return 0;
	return scores.sum() / scores.size();
}

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

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

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

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

ostream& operator <<(ostream& os, Student& st)
{
	os << st.name << "'s scores:\n";
	st.sco_out();
	return os;
}
Note that the function of double operator [](int)const and double& operator [](int) are different. The former has the const suffix, which means it can't change the value of any members. The latter return a reference, which allows you to modify members' value. More specifically:

                        cout << stu[2];  //invokes the former

                        stu[2] = 80;      //invokes the latter

As you can see, the whole implementation is very short, but function of this class is intact.Using the initializer list, you don't have to redefined your own constructor .Almost every public interface have invoked functions that were defined by others.

III. Testing Design

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

const int STUDENT_NUM = 6;
const int SCORES_NUM = 3;

using std::cin;
using std::cout;
using std::endl;

int main()
{
     valarray<double> d_scores(SCORES_NUM);
     d_scores[0] = 85; d_scores[1] = 90; d_scores[2] = 80;
     double e_scores[SCORES_NUM] = {76, 83, 94};

     Student stu[STUDENT_NUM] = 
     {
          Student(),                    //default constructor
          Student("a_stu"),             //student without score
          Student(SCORES_NUM),          //student without name
          Student("c_stu",SCORES_NUM),  //student have given name and empty score
          Student("d_stu",d_scores),            //enter scores by valarray object
          Student("e_stu",e_scores,SCORES_NUM)  //enter scores by ordinary array
     };

     cout << "Initialization Situation:\n";
     for(int i = 0; i< STUDENT_NUM; i++) cout << stu[i];

     cout << "Enter the second student's name: "; getline(cin, stu[2]);
     stu[2].setScores();	
     cout << "Modify the third student's name: "; cin >> stu[3];	
     stu[3].setScores();	
     cout << "The third score of " << stu[5].Name() << " is: " << stu[5][2] << endl;
     stu[4][1] = 87;

     cout << "Resault:\n";
     for(int i = 0; i< STUDENT_NUM; i++)
     {
          cout << stu[i];
          cout << "Average: " << stu[i].average();
          cout << endl;
     }
}
 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值