【C++ Primer】第十四章 C++中的代码重用
序: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);
复习题:虚基类与非虚基类之间的区别
如果两个继承路线有相同的祖先,则类中包含两个祖先的拷贝,而将基类设置成虚的则可以避免这种情况。