【C++ Primer】第十四章 C++中的代码重用

原创 2012年03月26日 20:57:41

序:C++的一个主要目标是促进代码重用,其中包含公有继承、包含、使用私有或保护继承

一,包含对象成员的类

       1)valarray类简介  #include <valarray>

             作用:处理数值,支持数值中所有元素的值相加,找最大值,最小值

             用法:vallarray <int>  a;               //数组 a  size=0

                        vallarray <double> b(10);   //数组  b  size=10

                        vallarray <double> c(10,8);   //数组  c  size=8  每个元素设置为 10

                        int  s={2,3,4,5,6};  vallarray <double> d(s,3); //数组d 取s的前三个元素

      2)student 类设计

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

using namespace std;
class student
{
private:
	typedef valarray<double> ArrayDb;//定义了一个double数组 的别名 
	string name;
	ArrayDb scores;//数组 
	ostream & arr_out(ostream &os)const;
public:
    /*已经初始化的类*/
	student():name("NULL Student"),scores(){};
	student(const string &s):name(s),scores(){};
	explicit student(int n):name("NULLy"),scores(n){}; //关闭隐式转换  因为n为数组个数 
	student(const string &s,int n):name(s),scores(n){};
	student(const string &s,ArrayDb a):name(s),scores(a){};//分数为数组 
	student(const char *str,const double *pd,int n):name(str),scores(pd,n){};
	
	~student(){cout<<"\nstudent is over";};
	double Average()const;
	const string &Name()const;
	double &operator[](int i);
	double operator[](int i)const;
	
	/*友元函数*/
	friend istream &operator>>(istream &is,student &stu);
	/*如果是成员函数,而不是友元函数的话,student>>cin 这样调用*/
	friend istream &getline(istream &is,student &stu);
	friend ostream &operator<<(ostream &os,const student &stu);	
	
};
 
    double student::Average()const  //求student 平均分 
    {
    	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];
	}
	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(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);
		return is;
	}
	ostream &operator<<(ostream &os,const student &stu)
	{
		os<<"scores for "<<stu.name<<":\n";
		stu.arr_out(os);
		return os;
	}	
	
	void set(student &s,int n)
	{
		cout<<"please enter the student's name:";
		getline(cin,s);
		cout<<"please enter "<<n<<"quiz scores:\n";
		for(int i=0;i<n;i++)
			cin>>s[i];
		
		while(cin.get()!='\n')
			continue;
	}
	int main()
	{
		student ada[3]={student(5),student(5),student(5)}; //初始化学生数组 
		int i;
		for(i=0;i<3;++i)
			set(ada[i],5); //初始化学生 
			
		cout<<"\nStudent List";
			
		for(i=0;i<3;++i) //学生名单 
			cout<<ada[i].Name()<<endl;
		
		cout<<"\nResult";
		
		for(i=0;i<3;++i) //学生平均分 
		{
			cout<<endl<<ada[i];
			cout<<"avaverage: "<<ada[i].Average()<<endl;
		}
		
		cout<<"\nDone.\n";
		
		return 0;
	}

1)typedef:用来声明自定义数据类型,配合各种原有数据类型来达到简化编程的目的的类型定义关键字。

typedef double double_a; //为已知类型 double起别名 double_a

typedef struct node{

char *name;

int age;

} *Node;

typedef valarray<double> ArrayDb;//定义了一个double数组 的别名

2)explicit : 关闭构造函数的隐式转换

explicit student(int n):name("NULLy"),scores(n){};

如果 student dod("xiaotian",10);//定义对象 名字为“xiaotian" 数组有5个元素

dod=5;//重新定义dod对象 名字为空,数组有5个元素

如果没有explicit 则调用student(5); 将5转化为一个临时student 对象

二,私有继承:has-a关系

1)class student : private string, private valarray<double>

不同种类的继承

特征 公有继承 保护继承 私有继承
共有成员变成 派生类的公有成员 派生类的保护成员 派生类的私有成员
保护成员变成 派生类的保护成员 派生类的保护成员 派生类的私有成员
私有成员变成 派生类的私有成员 派生类的私有成员 派生类的私有成员
能否隐式向上转换

使用using 重新定义访问权限,让私有继承中的方法也可以调用

class student :private string,private valarray<double>

{

public:

using std::valarray::mix; //using 声明只使用 成员名,没有圆括号

using std::valarray::min;

}

三,多重继承

#include <string>
#include <iostream>
using namespace std;
class worker
{
private:
	string fullname;
	long ID;
public:
	worker():fullname("no one"),ID(0L){};
	worker(const string &s, long n):fullname(s),ID(n){};
	virtual ~worker()=0;//  虚函数
	virtual void set();
	virtual void show() const;
} ;
class waiter:public worker   //服务员 
{
private:
	int panache;
public:
	waiter():worker(),panache(0){};
	waiter(const string &s,long n,int p=0):worker(s,n),panache(p){};
	waiter(const worker &wk,int p=0):worker(wk),panache(0){};
	void set();
	void show()const; 	
};
class singer:public worker
{
protected:
	enum{
		others,alto,constralto,soprano,bass,baritone,tenor
	};
	enum{Vtypes = 7};
private:
	static char *pv[Vtypes];
	int voice;
public:
	singer():worker(),voice(others){};
	singer(const string &s,long n,int v=others):worker(s,n),voice(others){};
	singer(const worker &wk,int v=others):worker(wk),voice(others){};
	void set();
	void show()const;
};
worker::~worker(){
	
}//虚函数必须实现
void worker::set()
{
	cout<<"Enter worker's name:";
	getline(cin,fullname);
	cout<<"Enter worker's ID: ";
	cin>>ID;
	while(cin.get()!='\n')//记住这里是字符而不是字符串
    	continue; 
} 
void worker::show()const
{
	cout<<"name:"<<fullname<<endl;
	cout<<"employee id"<<ID<<endl;
}
//waiter method
void waiter::set()
{
	worker::set();
	cout<<"Enter waiter's panache rating :";
	cin>>panache;
	while(cin.get()!='\n')//记住这里是字符而不是字符串
    	continue; 
}
void waiter::show()const
{
	cout<<"category:waiter\n";
	worker::show();
	cout<<"panache rating :"<<panache<<"\n";
}
//singer method
char *singer::pv[]={
		"other","alto","constralto","soprano","bass","baritone","tenor"
	};
void singer::set()
{
	worker::set();
	int i;
	for(i=0;i<Vtypes;++i)
	{
		cout<<i<<":"<<pv[i]<<"  ";
		if(i%4== 3)
			cout<<endl;
		
	}
	if(i%4==0)
		cout<<endl;
	cin>>voice;
	while(cin.get()!='\n')
		continue;
}
void singer::show()const
{
	cout<<"category:singer\n";
	worker::show();
	cout<<"vocal range :"<<pv[voice]<<endl;
	
}
const int LIM=4;
int main()
{
	waiter bob("bob apple",314L,5);
	singer bev("beverly hills",522L,3);
	waiter w_temp;
	singer s_temp;
	
	worker *pw[LIM]={&bob,&bev,&w_temp,&s_temp};
	
	int i;
	for(i=2;i<LIM;++i)
		pw[i]->set();

	for(i=0;i<LIM;i++)
	{
		pw[i]->show();
		cout<<endl;
	}
	
	return 0;
}

 1)singer类和waiter类都继承了一个worker组件,如果新建一个类singerwaiter,

class singerwaiter :public singer,public waiter{};

则singerwaiter包含两个worker组件,这将引起问题。例如,通常可以将派生类的对象的地址赋给基类指针但是现在回出现二义性。

singerwaiter ed;

worker *pw = &ed;//这 会出现二义性

解决办法:

1>worker *pw=(waiter *)&ed;

worker *pw=(singer *)&ed;

2>虚基类

class singer:virtual public worker{};

class waiter:public virtual worker{};

这时 class singerwaiter:public singer,public worker;

2)C++在基类是虚拟的时候,禁止信息通过中间类自动传递给基类

3)singerwaiter ss; ss.show();//引起二义性

ss.singer::show();//解决问题

四,类模板

1)template <class Type> //template 告诉编译器,将要定义一个模板。class 是类型名,Type 是该变量名

2)简单的模板使用

#include <iostream>
#include <string>
#include <cctype>
using namespace std;

template <class Type>
class  stack
{
private:	
	enum{MAX=10};
	Type items[MAX];
	int top;
public:
	stack();
	bool isempty();
	bool isfull();
	bool push(const Type &item);//add item to stack
	bool pop(Type &item);
};
template <class Type>
stack<Type>::stack()
{
	top=0;
}
template <class Type>
bool stack<Type>::isempty()
{
	return top==0;
}
template <class Type>
bool stack<Type>::isfull()
{
	return top==MAX;
}
template <class Type>
bool stack<Type>::push(const Type &item)
{
	if(top<MAX)
	{
		items[top++] = item;
		return true;
	}
	else
	{
		return false;
	}
}
template <class Type>
bool stack<Type>::pop(Type &item)
{
	if(top>0)
	{
		item=items[--top];
		return true;
	}
	else
	{
		return false;
	}
}
int main()
{
	stack<string> st; //????????????
	char ch;
	string po;
	cout<<"Please enter A to add a purchase order\n"<<"P to process a PO,or Q to quit.\n";
	while(cin>>ch&&std::toupper(ch)!='Q')
	{
		
		while(cin.get()!='\n')
			continue;
		
		if(!std::isalpha(ch))
		{
			cout<<'\a';
			continue;
		}
		
		switch(ch)
		{
			case 'A':
			case 'a':cout<<"Enter a po number to add:";
				     cin>>po;
					 if(st.isfull())
					 	cout<<"stack is already full"<<endl;
					 else
					 	st.push(po);
					 break;
			case 'p':
			case 'P':if(st.isempty())
			         	cout<<"stack already empty"<<endl;
					 else
					    {
    						st.pop(po);
    						cout<<"po#"<<po<<"popped\n";
    						break;
    					} 
		}
		cout<<"Please enter A to add a purchase order\n"<<"P to process a PO,or Q to quit.\n";
	} 
	
	cout<<"BYE\n";
	return 0;
}

说明:1)模板代码不能修改参数值,不能使用参数地址。所以不能使用n++和&n这种表达式

            2)递归使用模板:Array<Array<int , 5>,10>   a;//包含10个元素的数组,每个元素是包含5个元素的数组

                                            int a[10][5];  //含有十行,每行包含5个元素

            3)使用多个参数:template <class T1, class T2>  //声明

                                             class  pa

                                            {

                                                  T1  a;

                                                  T2  b;

                                             };

                      pa<string ,int>("tianshuai",1);

复习题:虚基类与非虚基类之间的区别

                如果两个继承路线有相同的祖先,则类中包含两个祖先的拷贝,而将基类设置成虚的则可以避免这种情况。




                        

             

《C++ primer plus》第十四章:C++的代码重用 学习笔记

《C++ primer plus》第十四章:C++的代码重用 学习笔记 这一章主要讲述了C++中的模板问题 1.valarray类的使用 valarray v1; int gpa[4] = ...

《C++ Primer Plus(第六版)》(30)(第十四章 C++中的代码重用 编程题答案)

14.7 编程练习 1.Wine类有一个string类对象成员(参见第4章)和一个Pair对象(参见本章):其中前者用来存储葡萄酒的名称,而后者有2个valarry对象(参见本章),这两个valar...

《C++ Primer Plus(第六版)》(29)(第十四章 C++中的代码重用 复习题答案)

14.6 复习题 1.以A栏的类为基类时,B栏的类采用共有派生还是私有派生更合适。 A B 派生类型 class Bear class PolarBear 公有派生 class Kit...

c++primer第十四章c++代码重用(二)

一、保护继承 当派生类中派生出另一个类时,私有继承和保护继承之间的主要区别: 使用私有继承,第三代两类将不能使用基类的接口,是因为基类的公有接口在派生类中将变为私有方法。 使用保护继承时,基类的...

c++primer第十四章--c++代码重用(一)

一、组合 (1)组合是has-a的关系,即创建一个包含其他类对象的类。 (2)使用公有继承时,类可以获得接口,但使用组合时,类可以获得实现,但不能获得接口,一般通过实现来调用类的方法。 (3)将...

2012/2/3 《C++ Primer Plus》第十四章:C++中的代码重用 学习笔记

《C++ Primer Plus》第十四章学习笔记   159:valarray类是由头文件valarray支持的,它支持诸如将数组中所有元素的值相加以及在数组中找出最大和最小值的操作,且它是一个...
  • Zyearn
  • Zyearn
  • 2012年02月03日 12:30
  • 912

c++ Primer Plus(第六版)第十四章习题,写代码之路

c++ Primer Plus(习题14.1) //书上的测试文件,相当于客户的使用说明书 //对于书上那个Pair对象,不认真的人都怀疑是否学过 //竟然是一个模板,这道题目想了很久很久,还是没思...
  • Robot_x
  • Robot_x
  • 2017年02月08日 19:57
  • 188

c++ primer(第五版)学习笔记及习题答案代码版(第十四章)重载运算与类型转换

笔记较为零散,都是自己不熟悉的知识点。 习题答案至于一个.h 和.cc 中,需要演示某一题直接修改 #define NUM****, 如运行14.30题为#define NUM1430;Alice ...
  • refuil
  • refuil
  • 2016年06月07日 08:42
  • 1433

第十四章 C++中的代码重用(2)

3. 多重继承 上节Student同时继承了String和Valarray类,这个就是多重继承了。这个功能很强大,因为跟实际相符(比如现在的平板,有时可以看出一个电脑,有时也可以归为手机),但用起来...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:【C++ Primer】第十四章 C++中的代码重用
举报原因:
原因补充:

(最多只允许输入30个字)