了解继承的概念之后,我们就来学习一下如何实现继承。
在第14章中我们说到,成员函数或成员数据可以是公有或者私有的。如果是公有的,那么它们可以被直接访问;如果是私有的,那么它们无法被直接访问。同时,我们还提到一个protected保留字,在没有使用继承的时候,它与private的效果是一样的,即无法被直接访问。如果使用了继承,我们就能体会到protected和private的差别。
private(私有)和protected(保护)都能实现类的封装性。private能够对外部和子类保密,即除了成员所在的类本身可以访问之外,别的都不能直接访问。protected能够对外部保密,但允许子类直接访问这些成员。public、private和protected对成员数据或成员函数的保护程度可以用下表来描述:
所以,当我们使用到继承的时候,必须考虑清楚:成员数据或成员函数到底应该是私有的还是保护的。
首先我们以一个学生类为例,介绍继承的写法:(程序17.3.1)
//student.h
#include <iostream>
using namespace std;
class student//学生类作为父类
{
public:
student(char *n,int a,int h,int w);//带参数的构造函数
student();//不带参数的构造函数
void set(char *n,int a,int h,int w);//设置
char * sname();
int sage();
int sheight();
int sweight();
protected:
char name[10];//姓名
int age;//年龄
int height;//身高
int weight;//体重
private:
int test;
};
char * student::sname()
{
return name;
}
int student::sage()
{
return age;
}
int student::sheight()
{
return height;
}
int student::sweight()
{
return weight;
}
void student::set(char *n,int a,int h,int w)
{
int i;
for (i=0;n[i]!='\0';i++)
{
name[i]=n[i];
}
name[i]='\0';
age=a;
height=h;
weight=w;
return;
}
student::student(char *n,int a,int h,int w)
{
cout <<"Constructing a student with parameter..." <<endl;
set(n,a,h,w);
}
student::student()
{
cout <<"Constructing a student without parameter..." <<endl;
}
//undergraduate.h
#include "student.h"
class Undergraduate:public student//本科生类作为子类,继承了学生类
{
public:
double score();
void setGPA(double g);//设置绩点
bool isAdult();//判断是否成年
protected:
double GPA;//本科生绩点
};
double Undergraduate::score()
{
return GPA;
}
void Undergraduate::setGPA(double g)
{
GPA=g;
return;
}
bool Undergraduate::isAdult()
{
return age>=18?true:false;//子类访问父类的保护成员数据
}
//main.cpp
#include <iostream>
#include "undergraduate.h"
using namespace std;
int main()
{
Undergraduate s1;//新建一个本科生对象
s1.set("Tom",21,178,60);
s1.setGPA(3.75);
cout <<s1.sname() <<endl;
cout <<s1.sage() <<endl;
cout <<s1.sheight() <<endl;
cout <<s1.sweight() <<endl;
cout <<s1.score() <<endl;
cout <<s1.isAdult() <<endl;
return 0;
}
运行结果:
Constructing a student without parameter...
Tom
21
178
60
3.75
1
在使用继承之前,我们必须保证父类是已经定义好的。如果父类是虚无的、没有被定义的,那么子类也就没什么好继承的了。定义一个子类的语法格式为:
class 子类名:继承方式父类名;
根据程序17.3.1的运行结果,我们可以清楚地看到,学生类里面的公有和保护成员都已经被继承到本科生类。本科生类可以使用学生类的成员函数,也可以访问学生类的保护成员。而本科生类中定义的成员则是对学生类的补充,并且也能够被使用。
在程序17.3.1中,我们选择的继承方式是public。和成员的类型一样,除了public之外,继承方式还有protected和private。那么,这三种继承方式到底有什么区别呢?
public是公有继承,或称为类型继承。它主要体现的是概念的延伸和扩展,父类所有的公有、保护成员都将按部就班地继承到子类中。父类的公有成员在子类中依然是公有的,父类的保护成员在子类中依然是保护的。比如程序17.3.1中的学生类和本科生类就是这样的关系。
private是私有继承,或称为私有的实现继承。它主要体现的是父类成员的重用。父类所有的公有、保护成员继承到子类时,类型会发生改变。父类的公有成员在子类中变成了私有成员,父类的保护成员在子类中也变成了私有成员。这时,我们可以利用从父类继承而来的成员函数来实现子类的成员函数,并且不必担心外部直接访问父类的成员函数,破坏了子类的秩序。比如我们认为栈是一种特殊的链表,它只能从链表尾部添加或删除结点,栈的压栈和退栈功能可以方便地由链表类的成员函数实现。但是,如果外部还能直接访问从链表类继承而来的成员函数,那么就可以在栈的任何位置插入结点,栈就会被破坏。
protected是保护继承,或称为保护的实现继承。与私有继承类似,它也是体现父类成员的重用。只不过父类的公有成员和保护成员在子类中都变成了保护成员。因此,如果有一个孙类继承了子类,那么父类中的成员也将被继承,成为孙类的保护成员。
public、private和protected三种继承方式可以用下表描述。其中右下角的九个单元格表示各种父类成员在对应的继承方式下,成为子类成员后的性质。
在使用继承的时候,我们必须根据实际需要选择合适的继承方式。下面我们以栈继承链表为例,理解一下私有继承方式:(程序17.3.2)
//node.h
#include <iostream>
using namespace std;
class Node
{
friend class Linklist;//链表类作为友元类
friend class Stack;//栈类作为友元类
public:
Node();
Node(Node &n);
Node(int i,char c='0');
Node(int i,char c,Node *p,Node *n);
~Node();
private:
int idata;
char cdata;
Node *prior;
Node *next;
};
Node::Node()
{
cout <<"Node constructor is running..." <<endl;
idata=0;
cdata='0';
prior=NULL;
next=NULL;
}
Node::Node(int i,char c)
{
cout <<"Node constructor is running..." <<endl;
idata=i;
cdata=c;
prior=NULL;
next=NULL;
}
Node::Node(int i,char c,Node *p,Node *n)
{
cout <<"Node constructor is running..." <<endl;
idata=i;
cdata=c;
prior=p;
next=n;
}
Node::Node(Node &n)
{
idata=n.idata;
cdata=n.cdata;
prior=n.prior;
next=n.next;
}
Node::~Node()
{
cout <<"Node destructor is running..." <<endl;
}
//linklist.h
#include "node.h"
#include <iostream>
using namespace std;
class Linklist
{
public:
Linklist(int i=0,char c='0');
Linklist(Linklist &l);
~Linklist();
bool Locate(int i);
bool Locate(char c);
bool Insert(int i=0,char c='0');
bool Delete();
void Show();
void Destroy();
protected://原私有成员改为保护成员,以便于Stack类继承
Node head;
Node * pcurrent;
};
Linklist::Linklist(int i,char c):head(i,c)
{
cout<<"Linklist constructor is running..."<<endl;
pcurrent=&head;
}
Linklist::Linklist(Linklist &l):head(l.head)
{
cout<<"Linklist Deep cloner running..." <<endl;
pcurrent=&head;
Node * ptemp1=l.head.next;
while(ptemp1!=NULL)
{
Node * ptemp2=new Node(ptemp1->idata,ptemp1->cdata,pcurrent,NULL);
pcurrent->next=ptemp2;
pcurrent=pcurrent->next;
ptemp1=ptemp1->next;
}
}
Linklist::~Linklist()
{
cout<<"Linklist destructor is running..."<<endl;
Destroy();
}
bool Linklist::Locate(int i)
{
Node * ptemp=&head;
while(ptemp!=NULL)
{
if(ptemp->idata==i)
{
pcurrent=ptemp;
return true;
}
ptemp=ptemp->next;
}
return false;
}
bool Linklist::Locate(char c)
{
Node * ptemp=&head;
while(ptemp!=NULL)
{
if(ptemp->cdata==c)
{
pcurrent=ptemp;
return true;
}
ptemp=ptemp->next;
}
return false;
}
bool Linklist::Insert(int i,char c)
{
if(pcurrent!=NULL)
{
Node * temp=new Node(i,c,pcurrent,pcurrent->next);
if (pcurrent->next!=NULL)
{
pcurrent->next->prior=temp;
}
pcurrent->next=temp;
return true;
}
else
{
return false;
}
}
bool Linklist::Delete()
{
if(pcurrent!=NULL && pcurrent!=&head)
{
Node * temp=pcurrent;
if (temp->next!=NULL)
{
temp->next->prior=pcurrent->prior;
}
temp->prior->next=pcurrent->next;
pcurrent=temp->prior;
delete temp;
return true;
}
else
{
return false;
}
}
void Linklist::Show()
{
Node * ptemp=&head;
while (ptemp!=NULL)
{
cout <<ptemp->idata <<'\t' <<ptemp->cdata <<endl;
ptemp=ptemp->next;
}
}
void Linklist::Destroy()
{
Node * ptemp1=head.next;
while (ptemp1!=NULL)
{
Node * ptemp2=ptemp1->next;
delete ptemp1;
ptemp1=ptemp2;
}
head.next=NULL;
}
//stack.h
#include "linklist.h"
class Stack:private Linklist//私有继承链表类
{
public:
bool push(int i,char c);
bool pop(int &i,char &c);
void show();
};
bool Stack::push(int i,char c)
{
while (pcurrent->next!=NULL)
pcurrent=pcurrent->next;
return Insert(i,c);//用链表类的成员函数实现功能
}
bool Stack::pop(int &i,char &c)
{
while (pcurrent->next!=NULL)
pcurrent=pcurrent->next;
i=pcurrent->idata;
c=pcurrent->cdata;
return Delete();//用链表类的成员函数实现功能
}
void Stack::show()
{
Show();//用链表类的成员函数实现功能
}
//main.cpp
#include <iostream>
#include "stack.h"
int main()
{
Stack ss;
int i,j;
char c;
for (j=0;j<3;j++)
{
cout <<"请输入一个数字和一个字母:" <<endl;
cin >>i >>c;
if (ss.push(i,c))
{
cout <<"压栈成功!" <<endl;
}
}
ss.show();
while (ss.pop(i,c))
{
cout <<"退栈数据为i=" <<i <<" c=" <<c <<endl;
}
return 0;
}
运行结果:
Node constructor is running...
Linklist constructor is running...
请输入一个数字和一个字母:1 a
Node constructor is running...
压栈成功!
请输入一个数字和一个字母:
2 b
Node constructor is running...
压栈成功!
请输入一个数字和一个字母:
3 c
Node constructor is running...
压栈成功!
0 0
1 a
2 b
3 c
Node destructor is running...
退栈数据为i=3 c=c
Node destructor is running...
退栈数据为i=2 c=b
Node destructor is running...
退栈数据为i=1 c=a
Linklist destructor is running...
Node destructor is running...
我们看到,Stack类私有继承了Linklist类之后,利用Linklist的成员函数,方便地实现了压栈和退栈功能。