2025年4月2日 星期三 乙巳(蛇)年 正月初三 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > VC/VC++

C++继承的实现和方式

时间:01-10来源:作者:点击数:85

了解继承的概念之后,我们就来学习一下如何实现继承。

私有和保护

在第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的成员函数,方便地实现了压栈和退栈功能。

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门